2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser;
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;
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;
32 * Abstract(base) Parser for DataContainerNodes e.g. ContainerNode, AugmentationNode.
34 public abstract class BaseDispatcherParser<E, P extends YangInstanceIdentifier.PathArgument, N extends DataContainerNode<P>, S>
35 implements ExtensibleParser<P, E, N, S> {
37 private final BuildingStrategy<P, N> buildingStrategy;
39 public BaseDispatcherParser(final BuildingStrategy<P, N> buildingStrategy) {
40 this.buildingStrategy = buildingStrategy;
43 public BaseDispatcherParser() {
44 this.buildingStrategy = new SimpleBuildingStrategy<>();
50 * @return New(empty) instance of a builder to build node identified by schema.
52 protected abstract DataContainerNodeBuilder<P, N> getBuilder(S 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
60 protected abstract DataSchemaNode getSchemaForChild(S schema, QName childQName);
65 * @return map from QName to child elements. Multiple elements are allowed under QName.
67 protected abstract LinkedListMultimap<QName, E> mapChildElements(Iterable<E> xml);
72 * @return map from QName to ChoiceNode schema of child nodes that are
73 * contained within a choice statement under current schema.
75 protected abstract Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(S schema);
80 * @return map from QName to child elements that are added by augmentation
81 * that targets current schema.
83 protected abstract Map<QName, AugmentationSchema> mapChildElementsFromAugments(S 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.
94 protected abstract Set<DataSchemaNode> getRealSchemasForAugment(S schema, AugmentationSchema augmentSchema);
98 * @return dispatcher object to dispatch parsing of child elements, might be
99 * the same instance if provided parsers are immutable.
101 protected abstract NodeParserDispatcher<E> getDispatcher();
104 * can return null only if you override ParsingStrategy and explicitely return null
111 public N parse(final Iterable<E> elements, final S schema) {
113 checkAtLeastOneNode(schema, elements);
115 DataContainerNodeBuilder<P, N> containerBuilder = getBuilder(schema);
117 // Map child nodes to QName
118 LinkedListMultimap<QName, E> mappedChildElements = mapChildElements(elements);
120 // Map child nodes from Augments
121 Map<QName, AugmentationSchema> mappedAugmentChildNodes = mapChildElementsFromAugments(schema);
122 LinkedListMultimap<AugmentationSchema, E> augmentsToElements = LinkedListMultimap.create();
124 // Map child nodes from choices
125 Map<QName, ChoiceSchemaNode> mappedChoiceChildNodes = mapChildElementsFromChoices(schema);
126 LinkedListMultimap<ChoiceSchemaNode, E> choicesToElements = LinkedListMultimap.create();
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",
133 ((AttributesBuilder<?>) containerBuilder).withAttributes(attributes);
137 if (schema instanceof ListSchemaNode) {
138 for (QName qname : ((ListSchemaNode) schema).getKeyDefinition()) {
139 if(mappedChildElements.get(qname.withoutRevision()).isEmpty()) {
143 DataSchemaNode childSchema = getSchemaForChild(schema, qname);
144 List<E> childrenForQName = mappedChildElements.removeAll(qname.withoutRevision());
147 DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> optionalChildNode = getDispatcher()
148 .dispatchChildElement(childSchema, childrenForQName);
149 if (optionalChildNode != null) {
150 containerBuilder.withChild(optionalChildNode);
155 //stage attribues for strategy before going deeper in the recursion
156 buildingStrategy.prepareAttributes(attributes, containerBuilder);
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) {
165 List<E> childrenForQName = mappedChildElements.get(childPartialQName);
168 if (isMarkedAs(mappedAugmentChildNodes, childSchema.getQName())) {
169 AugmentationSchema augmentationSchema = mappedAugmentChildNodes.get(childSchema.getQName());
170 augmentsToElements.putAll(augmentationSchema, childrenForQName);
172 } else if (isMarkedAs(mappedChoiceChildNodes, childSchema.getQName())) {
173 ChoiceSchemaNode choiceSchema = mappedChoiceChildNodes.get(childSchema.getQName());
174 choicesToElements.putAll(choiceSchema, childrenForQName);
175 // Regular child nodes
177 DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> optionalChildNode = getDispatcher()
178 .dispatchChildElement(childSchema, childrenForQName);
179 if (optionalChildNode != null) {
180 containerBuilder.withChild(optionalChildNode);
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);
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);
204 return buildingStrategy.build(containerBuilder);
208 public BuildingStrategy<P, N> getBuildingStrategy() {
209 return buildingStrategy;
212 protected Map<QName, String> getAttributes(final E e) {
213 return Collections.emptyMap();
216 protected boolean strictParsing() {
220 private static boolean isMarkedAs(final Map<QName, ?> mappedAugmentChildNodes, final QName qName) {
221 return mappedAugmentChildNodes.containsKey(qName);
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);
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);
235 public static class SimpleBuildingStrategy<P extends YangInstanceIdentifier.PathArgument, N extends DataContainerNode<P>> implements BuildingStrategy<P, N> {
237 public N build(final NormalizedNodeBuilder<P, ?, N> builder) {
238 return builder.build();
242 public void prepareAttributes(final Map<QName, String> attributes, final NormalizedNodeBuilder<P, ?, N> containerBuilder) {