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