/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedListMultimap; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder; import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema; /** * Abstract(base) Parser for DataContainerNodes e.g. ContainerNode, AugmentationNode. */ public abstract class BaseDispatcherParser, S> implements ExtensibleParser { private final BuildingStrategy buildingStrategy; public BaseDispatcherParser(final BuildingStrategy buildingStrategy) { this.buildingStrategy = buildingStrategy; } public BaseDispatcherParser() { this.buildingStrategy = new SimpleBuildingStrategy<>(); } /** * * @param schema * @return New(empty) instance of a builder to build node identified by schema. */ protected abstract DataContainerNodeBuilder getBuilder(S schema); /** * * @param schema * @param childQName QName of a child being parsed, QName does not continue revision date * @return schema object for child identified by parent schema: schema and QName childQName */ protected abstract DataSchemaNode getSchemaForChild(S schema, QName childQName); /** * * @param xml * @return map from QName to child elements. Multiple elements are allowed under QName. */ protected abstract LinkedListMultimap mapChildElements(Iterable xml); /** * * @param schema * @return map from QName to ChoiceNode schema of child nodes that are * contained within a choice statement under current schema. */ protected abstract Map mapChildElementsFromChoices(S schema); /** * * @param schema * @return map from QName to child elements that are added by augmentation * that targets current schema. */ protected abstract Map mapChildElementsFromAugments(S schema); /** * * @param schema * @param augmentSchema * @return Set of real schema objects that represent child nodes of an * augmentation. Augmentation schema child nodes, if further * augmented, do not contain further augmented, that are crucial for * parsing. The real schema object can be retrieved from parent schema: schema. */ protected abstract Set getRealSchemasForAugment(S schema, AugmentationSchema augmentSchema); /** * * @return dispatcher object to dispatch parsing of child elements, might be * the same instance if provided parsers are immutable. */ protected abstract NodeParserDispatcher getDispatcher(); /** * can return null only if you override ParsingStrategy and explicitely return null * @param elements * @param schema * @return */ @Nullable @Override public N parse(final Iterable elements, final S schema) { checkAtLeastOneNode(schema, elements); DataContainerNodeBuilder containerBuilder = getBuilder(schema); // Map child nodes to QName LinkedListMultimap mappedChildElements = mapChildElements(elements); // Map child nodes from Augments Map mappedAugmentChildNodes = mapChildElementsFromAugments(schema); LinkedListMultimap augmentsToElements = LinkedListMultimap.create(); // Map child nodes from choices Map mappedChoiceChildNodes = mapChildElementsFromChoices(schema); LinkedListMultimap choicesToElements = LinkedListMultimap.create(); Map attributes = getAttributes(elements.iterator().next()); if (containerBuilder instanceof AttributesBuilder) { final int size = Iterables.size(elements); Preconditions.checkArgument(size == 1, "Unexpected number of elements: %s, should be 1 for: %s", size, schema); ((AttributesBuilder) containerBuilder).withAttributes(attributes); } //parse keys first if (schema instanceof ListSchemaNode) { for (QName qname : ((ListSchemaNode) schema).getKeyDefinition()) { if(mappedChildElements.get(qname.withoutRevision()).isEmpty()) { continue; } DataSchemaNode childSchema = getSchemaForChild(schema, qname); List childrenForQName = mappedChildElements.removeAll(qname.withoutRevision()); DataContainerChild optionalChildNode = getDispatcher() .dispatchChildElement(childSchema, childrenForQName); if (optionalChildNode != null) { containerBuilder.withChild(optionalChildNode); } } } //stage attribues for strategy before going deeper in the recursion buildingStrategy.prepareAttributes(attributes, containerBuilder); // process Child nodes for (QName childPartialQName : mappedChildElements.keySet()) { DataSchemaNode childSchema = getSchemaForChild(schema, childPartialQName); //with strict parsing an exception would be already thrown, with nonstrict we want to ignore this node if (childSchema == null) { continue; } List childrenForQName = mappedChildElements.get(childPartialQName); // Augment if (isMarkedAs(mappedAugmentChildNodes, childSchema.getQName())) { AugmentationSchema augmentationSchema = mappedAugmentChildNodes.get(childSchema.getQName()); augmentsToElements.putAll(augmentationSchema, childrenForQName); // Choices } else if (isMarkedAs(mappedChoiceChildNodes, childSchema.getQName())) { ChoiceSchemaNode choiceSchema = mappedChoiceChildNodes.get(childSchema.getQName()); choicesToElements.putAll(choiceSchema, childrenForQName); // Regular child nodes } else { DataContainerChild optionalChildNode = getDispatcher() .dispatchChildElement(childSchema, childrenForQName); if (optionalChildNode != null) { containerBuilder.withChild(optionalChildNode); } } } // TODO ordering is not preserved for choice and augment elements for (ChoiceSchemaNode choiceSchema : choicesToElements.keySet()) { DataContainerChild optionalChild = getDispatcher() .dispatchChildElement(choiceSchema, choicesToElements.get(choiceSchema)); if (optionalChild != null) { containerBuilder.withChild(optionalChild); } } for (AugmentationSchema augmentSchema : augmentsToElements.keySet()) { Set realChildSchemas = getRealSchemasForAugment(schema, augmentSchema); EffectiveAugmentationSchema augSchemaProxy = new EffectiveAugmentationSchema(augmentSchema, realChildSchemas); DataContainerChild optionalChild = getDispatcher() .dispatchChildElement(augSchemaProxy, augmentsToElements.get(augmentSchema)); if (optionalChild != null) { containerBuilder.withChild(optionalChild); } } return buildingStrategy.build(containerBuilder); } @Override public BuildingStrategy getBuildingStrategy() { return buildingStrategy; } protected Map getAttributes(final E e) { return Collections.emptyMap(); } protected boolean strictParsing() { return true; } private boolean isMarkedAs(final Map mappedAugmentChildNodes, final QName qName) { return mappedAugmentChildNodes.containsKey(qName); } protected void checkOnlyOneNode(final S schema, final Iterable childNodes) { final int size = Iterables.size(childNodes); Preconditions.checkArgument(size == 1, "Node detected multiple times, should be 1, identified by: %s, found: %s", schema, childNodes); } private void checkAtLeastOneNode(final S schema, final Iterable childNodes) { Preconditions.checkArgument(!Iterables.isEmpty(childNodes), "Node detected 0 times, should be at least 1, identified by: %s, found: %s", schema, childNodes); } public static class SimpleBuildingStrategy

> implements BuildingStrategy { @Override public N build(final NormalizedNodeBuilder builder) { return builder.build(); } @Override public void prepareAttributes(final Map attributes, final NormalizedNodeBuilder containerBuilder) { // NOOP } } }