org.opendaylight.restconf.server.spi.DefaultMapBodyOrder Maven / Gradle / Ivy
/*
* 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