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

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

There is a newer version: 8.0.3
Show newest version
/*
 * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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 com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;

/**
 * Default, standards-compliant mode. {@code leaf} children referenced in predicates are first, followed by others
 * in iteration order.
 */
@NonNullByDefault
final class DefaultMapBodyOrder extends MapBodyOrder {
    static final DefaultMapBodyOrder INSTANCE = new DefaultMapBodyOrder();

    private DefaultMapBodyOrder() {
        // Hidden on purpose
    }

    @Override
    Iterable orderBody(final MapEntryNode entry) throws IOException {
        // First things first: we will need to size our two collections...
        final var keys = entry.name().keySet();
        final var keySize = keys.size();
        final var entrySize = entry.size();
        final var otherSize = entrySize - keySize;

        // ... and that allows us to establish some invariants and optimize based on them
        if (otherSize > 0) {
            return orderBody(entry, keys, keySize, otherSize);
        } else if (otherSize == 0) {
            return keySize == 1 ? orderKey(entry, keys.iterator().next()) : orderKeys(entry, keys, keySize);
        } else {
            throw new IOException(entry.name() + " requires " + keySize + " items, have only " + entrySize);
        }
    }

    private static Iterable orderBody(final MapEntryNode entry, final Set qnames,
            final int keySize, final int otherSize) throws IOException {
        final var keys = new ArrayList>(keySize);
        final var others = new ArrayList(otherSize);

        // Single-pass over children, classifying them into two parts.
        for (var child : entry.body()) {
            if (child instanceof LeafNode leaf && qnames.contains(qnameOf(leaf))) {
                keys.add(leaf);
            } else {
                others.add(child);
            }
        }

        // Check we have all the keys
        if (keys.size() != keySize) {
            throw new IOException("Missing leaf nodes for "
                + Sets.difference(qnames, keys.stream().map(DefaultMapBodyOrder::qnameOf).collect(Collectors.toSet()))
                + " in " + entry.prettyTree());
        }

        // Make sure key iteration order matches qnames, if not go through a sort
        if (!Iterators.elementsEqual(
                qnames.iterator(),
                Iterators.transform(keys.iterator(), DefaultMapBodyOrder::qnameOf))) {
            final var tmp = List.copyOf(qnames);
            keys.sort(Comparator.comparingInt(k -> tmp.indexOf(qnameOf(k))));
        }

        return Iterables.concat(keys, others);
    }

    private static Iterable orderKeys(final MapEntryNode entry, final Set qnames,
            final int keySize) throws IOException {
        // Every child is supposed to be a leaf, addressable via NodeIdentifier, just look each one up and be done with
        // it.
        final var keys = new ArrayList(keySize);
        for (var qname : qnames) {
            keys.add(requireKeyLeaf(entry, qname));
        }
        return keys;
    }

    private static Collection orderKey(final MapEntryNode entry, final QName key)
            throws IOException {
        requireKeyLeaf(entry, key);
        return entry.body();
    }

    private static LeafNode requireKeyLeaf(final MapEntryNode entry, final QName key) throws IOException {
        final var child = entry.childByArg(new NodeIdentifier(key));
        if (child instanceof LeafNode leaf) {
            return leaf;
        } else if (child == null) {
            throw new IOException("No leaf for " + key + " in " + entry.prettyTree());
        } else {
            throw new IOException("Child " + child.prettyTree() + " is not a leaf");
        }
    }

    private static QName qnameOf(final NormalizedNode node) {
        return node.name().getNodeType();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy