org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.BaseDispatcherParser.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.BaseDispatcherParser.java

Source

/*
 * 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<E, P extends YangInstanceIdentifier.PathArgument, N extends DataContainerNode<P>, S>
        implements ExtensibleParser<P, E, N, S> {

    private final BuildingStrategy<P, N> buildingStrategy;

    public BaseDispatcherParser(final BuildingStrategy<P, N> buildingStrategy) {
        this.buildingStrategy = buildingStrategy;
    }

    public BaseDispatcherParser() {
        this.buildingStrategy = new SimpleBuildingStrategy<>();
    }

    /**
     *
     * @param schema schema belonging to the type N of NormalizedNode
     * @return New(empty) instance of a builder to build node identified by schema.
     */
    protected abstract DataContainerNodeBuilder<P, N> getBuilder(S schema);

    /**
     *
     * @param schema schema belonging to the type N of NormalizedNode
     * @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 elements elements to be parsed into NormalizedNode
     * @return map from QName to child elements. Multiple elements are allowed under QName.
     */
    protected abstract LinkedListMultimap<QName, E> mapChildElements(Iterable<E> elements);

    /**
     *
     * @param schema schema belonging to the type N of NormalizedNode
     * @return map from QName to ChoiceNode schema of child nodes that are
     *         contained within a choice statement under current schema.
     */
    protected abstract Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(S schema);

    /**
     *
     * @param schema schema belonging to the type N of NormalizedNode
     * @return map from QName to child elements that are added by augmentation
     *         that targets current schema.
     */
    protected abstract Map<QName, AugmentationSchema> mapChildElementsFromAugments(S schema);

    /**
     *
     * @param schema schema belonging to the type N of NormalizedNode
     * @param augmentSchema 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<DataSchemaNode> 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<E> getDispatcher();

    /**
     * can return null only if you override ParsingStrategy and explicitely return null
     * @param elements elements to be parsed into NormalizedNode
     * @param schema schema belonging to the type N of NormalizedNode
     * @return child of DataContainerNode as a result of parsing list of E elements with schema S
     */
    @Nullable
    @Override
    public N parse(final Iterable<E> elements, final S schema) {

        checkAtLeastOneNode(schema, elements);

        DataContainerNodeBuilder<P, N> containerBuilder = getBuilder(schema);

        // Map child nodes to QName
        LinkedListMultimap<QName, E> mappedChildElements = mapChildElements(elements);

        // Map child nodes from Augments
        Map<QName, AugmentationSchema> mappedAugmentChildNodes = mapChildElementsFromAugments(schema);
        LinkedListMultimap<AugmentationSchema, E> augmentsToElements = LinkedListMultimap.create();

        // Map child nodes from choices
        Map<QName, ChoiceSchemaNode> mappedChoiceChildNodes = mapChildElementsFromChoices(schema);
        LinkedListMultimap<ChoiceSchemaNode, E> choicesToElements = LinkedListMultimap.create();

        Map<QName, String> 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()) {
                final QName noRev = qname.withoutRevision();
                if (mappedChildElements.get(noRev).isEmpty()) {
                    continue;
                }

                DataSchemaNode childSchema = getSchemaForChild(schema, qname);
                List<E> childrenForQName = mappedChildElements.removeAll(noRev);

                DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> 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<E> 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<? extends YangInstanceIdentifier.PathArgument, ?> 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<? extends YangInstanceIdentifier.PathArgument, ?> optionalChild = getDispatcher()
                    .dispatchChildElement(choiceSchema, choicesToElements.get(choiceSchema));
            if (optionalChild != null) {
                containerBuilder.withChild(optionalChild);
            }
        }

        for (AugmentationSchema augmentSchema : augmentsToElements.keySet()) {
            Set<DataSchemaNode> realChildSchemas = getRealSchemasForAugment(schema, augmentSchema);
            EffectiveAugmentationSchema augSchemaProxy = new EffectiveAugmentationSchema(augmentSchema,
                    realChildSchemas);
            DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> optionalChild = getDispatcher()
                    .dispatchChildElement(augSchemaProxy, augmentsToElements.get(augmentSchema));
            if (optionalChild != null) {
                containerBuilder.withChild(optionalChild);
            }
        }

        return buildingStrategy.build(containerBuilder);
    }

    @Override
    public BuildingStrategy<P, N> getBuildingStrategy() {
        return buildingStrategy;
    }

    protected Map<QName, String> getAttributes(final E e) {
        return Collections.emptyMap();
    }

    protected boolean strictParsing() {
        return true;
    }

    private static boolean isMarkedAs(final Map<QName, ?> mappedAugmentChildNodes, final QName qName) {
        return mappedAugmentChildNodes.containsKey(qName);
    }

    protected void checkOnlyOneNode(final S schema, final Iterable<E> 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<E> 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<P extends YangInstanceIdentifier.PathArgument, N extends DataContainerNode<P>>
            implements BuildingStrategy<P, N> {
        @Override
        public N build(final NormalizedNodeBuilder<P, ?, N> builder) {
            return builder.build();
        }

        @Override
        public void prepareAttributes(final Map<QName, String> attributes,
                final NormalizedNodeBuilder<P, ?, N> containerBuilder) {
            // NOOP
        }
    }
}