Skip to content

Commit

Permalink
Clean up DataSchemaContextNode.Composite
Browse files Browse the repository at this point in the history
Fix naming of methods to be consistent with the rest our codebase. Also
document behaviour and eliminate duplicate code.

JIRA: YANGTOOLS-1413
Change-Id: I31bdd01a8bcb2ab8046bdbe7880a422c1e2e8dc0
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
  • Loading branch information
rovarga committed May 21, 2023
1 parent cc41a96 commit 2c1ebba
Show file tree
Hide file tree
Showing 16 changed files with 162 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ protected final String serializeImpl(final YangInstanceIdentifier data) {
DataSchemaContextNode current = getDataContextTree().getRoot();
QNameModule lastModule = null;
for (var arg : data.getPathArguments()) {
current = current instanceof Composite composite ? composite.getChild(arg) : null;
current = current instanceof Composite composite ? composite.childByArg(arg) : null;
checkArgument(current != null, "Invalid input %s: schema for argument %s (after %s) not found", data, arg,
sb);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/
package org.opendaylight.yangtools.yang.data.util;

import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.QName;
Expand All @@ -22,7 +21,6 @@
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode.Composite;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode.PathMixin;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode.SimpleValue;
import org.opendaylight.yangtools.yang.data.util.impl.model.AbstractCompositeContextNode;
import org.opendaylight.yangtools.yang.data.util.impl.model.AbstractDataSchemaContextNode;
Expand All @@ -40,19 +38,19 @@
* {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} and serialization format defined in RFC6020,
* since the mapping is not one-to-one.
*/
public sealed interface DataSchemaContextNode permits AbstractDataSchemaContextNode, Composite, PathMixin, SimpleValue {
public sealed interface DataSchemaContextNode permits AbstractDataSchemaContextNode, Composite, SimpleValue {
/**
* A {@link DataSchemaContextNode} containing other {@link DataSchemaContextNode}s.
*/
sealed interface Composite extends DataSchemaContextNode permits AbstractCompositeContextNode {
sealed interface Composite extends DataSchemaContextNode permits PathMixin, AbstractCompositeContextNode {
/**
* Find a child node identifier by its {@link PathArgument}.
*
* @param child Child path argument
* @return A child node, or null if not found
* @param arg Child path argument
* @return A child node, or {@code null} if not found
* @throws NullPointerException if {@code arg} is {@code null}
*/
// FIXME: YANGTOOLS-1413: document PathArgument type mismatch, nullness and also rename it to 'childForArg'
@Nullable DataSchemaContextNode getChild(PathArgument child);
@Nullable DataSchemaContextNode childByArg(PathArgument arg);

/**
* Find a child node identifier by its {code data tree} {@link QName}. This method returns intermediate nodes
Expand All @@ -61,38 +59,33 @@ sealed interface Composite extends DataSchemaContextNode permits AbstractComposi
* visible in RFC7950 XML encoding, and a further call to this method with the same {@code child} argument will
* provide the next step.
*
* @param child Child data tree QName
* @return A child node, or null if not found
* @param qname Child data tree QName
* @return A child node, or {@code null} if not found
* @throws NullPointerException if {@code arg} is {@code null}
*/
// FIXME: YANGTOOLS-1413: document child == null, also rename to 'childForQName'
@Nullable DataSchemaContextNode getChild(QName child);
@Nullable DataSchemaContextNode childByQName(QName qname);

/**
* Find a child node as identified by a {@link YangInstanceIdentifier} relative to this node.
*
* @param path Path towards the child node
* @return Child node if present, or empty when corresponding child is not found.
* @throws NullPointerException if {@code path} is null
* @throws NullPointerException if {@code path} is {@code null}
*/
// FIXME: YANGTOOLS-1413: rename add a childForPath() method and rename this to 'findChildForPath'
default @NonNull Optional<@NonNull DataSchemaContextNode> findChild(
final @NonNull YangInstanceIdentifier path) {
default @Nullable DataSchemaContextNode childByPath(final @NonNull YangInstanceIdentifier path) {
final var it = path.getPathArguments().iterator();
if (!it.hasNext()) {
return Optional.of(this);
return this;
}

var current = this;
while (true) {
final var child = current.getChild(it.next());
if (child == null) {
return Optional.empty();
}
if (!it.hasNext()) {
return Optional.of(child);
final var child = current.childByArg(it.next());
if (child == null || !it.hasNext()) {
return child;
}
if (!(child instanceof Composite compositeChild)) {
return Optional.empty();
return null;
}
current = compositeChild;
}
Expand Down Expand Up @@ -140,7 +133,7 @@ sealed interface Composite extends DataSchemaContextNode permits AbstractComposi
* <p>
* This trait is important for XML codec, but also for JSON encoding of {@link YangInstanceIdentifier}.
*/
sealed interface PathMixin extends DataSchemaContextNode permits AbstractMixinContextNode {
sealed interface PathMixin extends Composite permits AbstractMixinContextNode {
/**
* The mixed-in {@link NodeIdentifier}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ private DataSchemaContextTree(final EffectiveModelContext ctx) {
* @throws NullPointerException if {@code path} is null
*/
public @NonNull Optional<@NonNull DataSchemaContextNode> findChild(final @NonNull YangInstanceIdentifier path) {
return root.findChild(path);
// Optional.ofNullable() inline due to annotations
final var child = root.childByPath(path);
return child == null ? Optional.empty() : Optional.of(child);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,19 @@ private PathArgument computeNextArgument() {
return computeIdentifierWithPredicate(name);
}

private DataSchemaContextNode nextContextNode(final QName name) {
current = getChild(current, name);
checkValid(current != null, "%s is not correct schema node identifier.", name);
private DataSchemaContextNode nextContextNode(final QName qname) {
current = getChild(current, qname);
checkValid(current != null, "%s is not correct schema node identifier.", qname);
while (current instanceof PathMixin mixin) {
product.add(mixin.mixinPathStep());
current = getChild(current, name);
current = getChild(current, qname);
}
stack.enterDataTree(name);
stack.enterDataTree(qname);
return current;
}

private static DataSchemaContextNode getChild(final DataSchemaContextNode parent, final QName name) {
return parent instanceof Composite composite ? composite.getChild(name) : null;
private static DataSchemaContextNode getChild(final DataSchemaContextNode parent, final QName qname) {
return parent instanceof Composite composite ? composite.childByQName(qname) : null;
}

/**
Expand Down Expand Up @@ -176,7 +176,7 @@ private PathArgument computeIdentifierWithPredicate(final QName name) {
type -> resolveLeafref(currentSchema.getQName(), type), keyValue);
return new NodeWithValue<>(name, value);
}
final var keyNode = currentNode instanceof Composite composite ? composite.getChild(key) : null;
final var keyNode = currentNode instanceof Composite composite ? composite.childByQName(key) : null;
if (keyNode == null) {
throw iae("%s is not correct schema node identifier.", key);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,85 @@

import static java.util.Objects.requireNonNull;

import org.eclipse.jdt.annotation.NonNull;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode.Composite;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;

public abstract sealed class AbstractCompositeContextNode extends AbstractDataSchemaContextNode implements Composite
permits AbstractMixinContextNode, DataContainerContextNode {
AbstractCompositeContextNode(final NodeIdentifier pathStep, final DataSchemaNode schema) {
permits ListItemContextNode, ContainerContextNode {
// FIXME: ImmutableMaps with compare-and-swap updates
private final ConcurrentMap<PathArgument, AbstractDataSchemaContextNode> byArg = new ConcurrentHashMap<>();
private final ConcurrentMap<QName, AbstractDataSchemaContextNode> byQName = new ConcurrentHashMap<>();
private final DataNodeContainer container;

AbstractCompositeContextNode(final NodeIdentifier pathStep, final DataNodeContainer container,
final DataSchemaNode schema) {
super(pathStep, schema);
this.container = requireNonNull(container);
}

@Override
public final DataSchemaContextNode enterChild(final SchemaInferenceStack stack, final QName child) {
return enterChild(requireNonNull(child), requireNonNull(stack));
public final AbstractDataSchemaContextNode childByArg(final PathArgument arg) {
final var existing = byArg.get(requireNonNull(arg));
if (existing != null) {
return existing;
}
return register(fromLocalSchema(arg));
}

@Override
public final DataSchemaContextNode enterChild(final SchemaInferenceStack stack,
final PathArgument child) {
return enterChild(requireNonNull(child), requireNonNull(stack));
public final AbstractDataSchemaContextNode childByQName(final QName qname) {
var existing = byQName.get(requireNonNull(qname));
if (existing != null) {
return existing;
}
return register(fromLocalSchemaAndQName(container, qname));
}

abstract @Nullable DataSchemaContextNode enterChild(@NonNull QName child, @NonNull SchemaInferenceStack stack);
@Override
public final DataSchemaContextNode enterChild(final SchemaInferenceStack stack, final QName qname) {
return pushToStack(stack, childByQName(qname));
}

abstract @Nullable DataSchemaContextNode enterChild(@NonNull PathArgument child,
@NonNull SchemaInferenceStack stack);
}
@Override
public final DataSchemaContextNode enterChild(final SchemaInferenceStack stack, final PathArgument arg) {
return pushToStack(stack, childByArg(arg));
}

private static @Nullable DataSchemaContextNode pushToStack(final SchemaInferenceStack stack,
final @Nullable AbstractDataSchemaContextNode child) {
requireNonNull(stack);
if (child != null) {
child.pushToStack(stack);
}
return child;
}

private AbstractDataSchemaContextNode fromLocalSchema(final PathArgument child) {
return fromSchemaAndQNameChecked(container, child.getNodeType());
}

private static AbstractDataSchemaContextNode fromLocalSchemaAndQName(final DataNodeContainer schema,
final QName child) {
return fromSchemaAndQNameChecked(schema, child);
}

private AbstractDataSchemaContextNode register(final AbstractDataSchemaContextNode potential) {
if (potential != null) {
// FIXME: use putIfAbsent() to make sure we do not perform accidental overrwrites
byArg.put(potential.getPathStep(), potential);
for (QName qname : potential.qnameIdentifiers()) {
byQName.put(qname, potential);
}
}
return potential;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
* since the mapping is not one-to-one.
*/
public abstract sealed class AbstractDataSchemaContextNode implements DataSchemaContextNode
permits AbstractCompositeContextNode, LeafContextNode, LeafListItemContextNode, OpaqueContextNode {
permits AbstractMixinContextNode, AbstractCompositeContextNode,
LeafContextNode, LeafListItemContextNode, OpaqueContextNode {
private final @Nullable NodeIdentifier pathStep;

final @NonNull DataSchemaNode dataSchemaNode;
Expand Down Expand Up @@ -69,16 +70,16 @@ void pushToStack(final @NonNull SchemaInferenceStack stack) {
stack.enterSchemaTree(dataSchemaNode.getQName());
}

static DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
static AbstractDataSchemaContextNode fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
return lenientOf(findChildSchemaNode(schema, child));
}

private static DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
final DataSchemaNode potential = parent.dataChildByName(child);
return potential == null ? findChoice(Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class), child)
: potential;
}

static AbstractDataSchemaContextNode fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
return lenientOf(findChildSchemaNode(schema, child));
}

// FIXME: this looks like it should be a Predicate on a stream with findFirst()
private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
for (ChoiceSchemaNode choice : choices) {
Expand Down Expand Up @@ -113,7 +114,7 @@ private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choi
}

// FIXME: do we tolerate null argument? do we tolerate unknown subclasses?
static @Nullable AbstractDataSchemaContextNode lenientOf(final @Nullable DataSchemaNode schema) {
private static @Nullable AbstractDataSchemaContextNode lenientOf(final @Nullable DataSchemaNode schema) {
if (schema instanceof ContainerLike containerLike) {
return new ContainerContextNode(containerLike);
} else if (schema instanceof ListSchemaNode list) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
*/
package org.opendaylight.yangtools.yang.data.util.impl.model;

import static java.util.Objects.requireNonNull;

import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
Expand All @@ -19,19 +22,29 @@
* levels backed by a single {@link DataSchemaNode}.
*/
abstract sealed class AbstractListLikeContextNode extends AbstractMixinContextNode
permits ListContextNode, LeafListContextNode, MapContextNode {
AbstractListLikeContextNode(final DataSchemaNode schema) {
permits LeafListContextNode, ListContextNode, MapContextNode {
private final @NonNull AbstractDataSchemaContextNode child;

AbstractListLikeContextNode(final DataSchemaNode schema, final AbstractDataSchemaContextNode child) {
super(schema);
this.child = requireNonNull(child);
}

@Override
public final AbstractDataSchemaContextNode childByQName(final QName qname) {
return qname.equals(dataSchemaNode.getQName()) ? child : null;
}

// Stack is already pointing to the corresponding statement, now we are just working with the child
@Override
final DataSchemaContextNode enterChild(final QName child, final SchemaInferenceStack stack) {
// Stack is already pointing to the corresponding statement, now we are just working with the child
return getChild(child);
public final AbstractDataSchemaContextNode enterChild(final SchemaInferenceStack stack, final QName qname) {
requireNonNull(stack);
return childByQName(qname);
}

@Override
final DataSchemaContextNode enterChild(final PathArgument child, final SchemaInferenceStack stack) {
return getChild(child);
public final DataSchemaContextNode enterChild(final SchemaInferenceStack stack, final PathArgument arg) {
requireNonNull(stack);
return childByArg(arg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode.PathMixin;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;

public abstract sealed class AbstractMixinContextNode extends AbstractCompositeContextNode implements PathMixin
public abstract sealed class AbstractMixinContextNode extends AbstractDataSchemaContextNode implements PathMixin
permits AbstractListLikeContextNode, ChoiceContextNode {
AbstractMixinContextNode(final DataSchemaNode schema) {
super(NodeIdentifier.create(schema.getQName()), schema);
Expand Down
Loading

0 comments on commit 2c1ebba

Please sign in to comment.