All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.restconf.server.spi.DefaultNormalizedNodeWriter Maven / Gradle / Ivy

There is a newer version: 8.0.3
Show newest version
/*
 * Copyright (c) 2016 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.restconf.server.spi;

import java.io.IOException;
import java.util.List;
import java.util.Set;
import javax.xml.transform.dom.DOMSource;
import org.opendaylight.restconf.api.query.DepthParam;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
import org.opendaylight.yangtools.yang.data.api.schema.AnyxmlNode;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;

/**
 * This is an experimental iterator over a {@link NormalizedNode}. This is essentially
 * the opposite of a {@link javax.xml.stream.XMLStreamReader} -- unlike instantiating an iterator over
 * the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows
 * us to write multiple nodes.
 */
// FIXME: this is a copy&paste from yangtools' NormalizedNodeWriter then adapted for filtering
final class DefaultNormalizedNodeWriter extends NormalizedNodeWriter {
    private static final QName ROOT_DATA_QNAME = QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "data");

    private final MapBodyOrder mapBodyOrder;
    private final Integer maxDepth;
    private final List> fields;

    private int currentDepth = 0;

    DefaultNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final boolean iterationOrder,
            final DepthParam depth, final List> fields) {
        super(writer);
        mapBodyOrder = iterationOrder ? IterationMapBodyOrder.INSTANCE : DefaultMapBodyOrder.INSTANCE;
        maxDepth = depth == null ? null : depth.value();
        this.fields = fields;
    }

    @Override
    protected void writeAnydata(final AnydataNode node) throws IOException {
        final var objectModel = node.bodyObjectModel();
        if (writer.startAnydataNode(node.name(), objectModel)) {
            writer.scalarValue(node.body());
            writer.endNode();
        }
    }

    @Override
    protected void writeAnyxml(final AnyxmlNode node) throws IOException {
        final var objectModel = node.bodyObjectModel();
        if (writer.startAnyxmlNode(node.name(), objectModel)) {
            if (DOMSource.class.isAssignableFrom(objectModel)) {
                writer.domSourceValue((DOMSource) node.body());
            } else {
                writer.scalarValue(node.body());
            }
            writer.endNode();
        }
    }

    @Override
    protected void writeChoice(final ChoiceNode node) throws IOException {
        writer.startChoiceNode(node.name(), node.size());
        writeChildren(node.body(), true);
    }

    @Override
    protected void writeContainer(final ContainerNode node) throws IOException {
        if (!node.name().getNodeType().withoutRevision().equals(ROOT_DATA_QNAME)) {
            writer.startContainerNode(node.name(), node.size());
            currentDepth++;
            writeChildren(node.body(), false);
            currentDepth--;
        } else {
            // write child nodes of data root container
            for (var child : node.body()) {
                currentDepth++;
                if (selectedByParameters(child, false)) {
                    write(child);
                }
                currentDepth--;
            }
        }
    }

    @Override
    protected void writeLeaf(final LeafNode node) throws IOException {
        writer.startLeafNode(node.name());
        writer.scalarValue(node.body());
        writer.endNode();
    }

    @Override
    protected void writeLeafSet(final LeafSetNode node) throws IOException {
        final var ordering = node.ordering();
        switch (ordering) {
            case SYSTEM -> writer.startLeafSet(node.name(), node.size());
            case USER -> writer.startOrderedLeafSet(node.name(), node.size());
            default -> throw new IOException("Unsupported ordering " + ordering.argument());
        }

        currentDepth++;
        writeChildren(node.body(), true);
        currentDepth--;
    }

    @Override
    protected void writeLeafSetEntry(final LeafSetEntryNode node) throws IOException {
        if (selectedByParameters(node, false)) {
            writer.startLeafSetEntryNode(node.name());
            writer.scalarValue(node.body());
            writer.endNode();
        }
    }

    @Override
    protected void writeMap(final MapNode node) throws IOException {
        final var ordering = node.ordering();
        switch (ordering) {
            case SYSTEM -> writer.startMapNode(node.name(), node.size());
            case USER -> writer.startOrderedMapNode(node.name(), node.size());
            default -> throw new IOException("Unsupported ordering " + ordering.argument());
        }
        writeChildren(node.body(), true);
    }

    @Override
    protected void writeMapEntry(final MapEntryNode node) throws IOException {
        writer.startMapEntryNode(node.name(), node.size());
        if (selectedByParameters(node, false)) {
            currentDepth++;
            writeChildren(mapBodyOrder.orderBody(node), false);
            currentDepth--;
        } else {
            writer.endNode();
        }
    }

    @Override
    protected void writeUnkeyedList(final UnkeyedListNode node) throws IOException {
        writer.startUnkeyedList(node.name(), node.size());
        writeChildren(node.body(), false);
    }

    @Override
    protected void writeUnkeyedListEntry(final UnkeyedListEntryNode node) throws IOException {
        writer.startUnkeyedListItem(node.name(), node.size());
        currentDepth++;
        writeChildren(node.body(), false);
        currentDepth--;
    }

    /**
     * Check if node should be written according to parameters fields and depth.
     * See Restconf draft.
     * @param node Node to be written
     * @param mixinParent {@code true} if parent is mixin, {@code false} otherwise
     * @return {@code true} if node will be written, {@code false} otherwise
     */
    private boolean selectedByParameters(final NormalizedNode node, final boolean mixinParent) {
        // nodes to be written are not limited by fields, only by depth
        if (fields == null) {
            return maxDepth == null || currentDepth < maxDepth;
        }

        // children of mixin nodes are never selected in fields but must be written if they are first in selected target
        if (mixinParent && currentDepth == 0) {
            return true;
        }

        // write only selected nodes
        if (currentDepth > 0 && currentDepth <= fields.size()) {
            return fields.get(currentDepth - 1).contains(node.name().getNodeType());
        }

        // after this depth only depth parameter is used to determine when to write node
        return maxDepth == null || currentDepth < maxDepth;
    }

    /**
     * Emit events for all children and then emit an endNode() event.
     *
     * @param children Child iterable
     * @param mixinParent {@code true} if parent is mixin, {@code false} otherwise
     * @throws IOException when the writer reports it
     */
    private void writeChildren(final Iterable children, final boolean mixinParent)
            throws IOException {
        for (var child : children) {
            if (selectedByParameters(child, mixinParent)) {
                write(child);
            }
        }
        writer.endNode();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy