Abstract infrastructure for normalized nodes translation.
[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 java.util.List;
11 import java.util.Map;
12 import java.util.Set;
13
14 import org.opendaylight.yangtools.yang.common.QName;
15 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
16 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
17 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
18 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
19 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
20 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.AugmentationSchemaProxy;
21 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
22 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
24
25 import com.google.common.base.Preconditions;
26 import com.google.common.collect.Iterables;
27 import com.google.common.collect.LinkedListMultimap;
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, ChoiceNode> 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(Iterable<E> element, S schema) {
93
94         checkAtLeastOneNode(schema, element);
95
96         DataContainerNodeBuilder<?, N> containerBuilder = getBuilder(schema);
97
98         // Map child nodes to QName
99         LinkedListMultimap<QName, E> mappedChildElements = mapChildElements(element);
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, ChoiceNode> mappedChoiceChildNodes = mapChildElementsFromChoices(schema);
107         LinkedListMultimap<ChoiceNode, E> choicesToElements = LinkedListMultimap.create();
108
109         // process Child nodes
110         for (QName childPartialQName : mappedChildElements.keySet()) {
111             DataSchemaNode childSchema = getSchemaForChild(schema, childPartialQName);
112             List<E> childrenForQName = mappedChildElements.get(childPartialQName);
113
114             // Augment
115             if (isMarkedAs(mappedAugmentChildNodes, childSchema.getQName())) {
116                 AugmentationSchema augmentationSchema = mappedAugmentChildNodes.get(childSchema.getQName());
117                 augmentsToElements.putAll(augmentationSchema, childrenForQName);
118                 // Choices
119             } else if (isMarkedAs(mappedChoiceChildNodes, childSchema.getQName())) {
120                 ChoiceNode choiceSchema = mappedChoiceChildNodes.get(childSchema.getQName());
121                 choicesToElements.putAll(choiceSchema, childrenForQName);
122                 // Regular child nodes
123             } else {
124                 DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> builtChildNode = getDispatcher()
125                         .dispatchChildElement(childSchema, childrenForQName);
126                 containerBuilder.withChild(builtChildNode);
127             }
128         }
129
130         // TODO ordering is not preserved for choice and augment elements
131         for (ChoiceNode choiceSchema : choicesToElements.keySet()) {
132             containerBuilder.withChild(getDispatcher().dispatchChildElement(choiceSchema,
133                     choicesToElements.get(choiceSchema)));
134         }
135
136         for (AugmentationSchema augmentSchema : augmentsToElements.keySet()) {
137             Set<DataSchemaNode> realChildSchemas = getRealSchemasForAugment(schema, augmentSchema);
138             AugmentationSchemaProxy augSchemaProxy = new AugmentationSchemaProxy(augmentSchema, realChildSchemas);
139             containerBuilder.withChild(getDispatcher().dispatchChildElement(augSchemaProxy, augmentsToElements.get(augmentSchema)));
140         }
141
142         return containerBuilder.build();
143     }
144
145     private boolean isMarkedAs(Map<QName, ?> mappedAugmentChildNodes, QName qName) {
146         return mappedAugmentChildNodes.containsKey(qName);
147     }
148
149     protected void checkOnlyOneNode(S schema, Iterable<E> childNodes) {
150         final int size = Iterables.size(childNodes);
151         Preconditions.checkArgument(size == 1,
152                 "Node detected multiple times, should be 1, identified by: %s, found: %s", schema, childNodes);
153     }
154
155     private void checkAtLeastOneNode(S schema, Iterable<E> childNodes) {
156         Preconditions.checkArgument(Iterables.isEmpty(childNodes) == false,
157                 "Node detected 0 times, should be at least 1, identified by: %s, found: %s", schema, childNodes);
158     }
159 }