/*
* Copyright (c) 2023 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.mdsal.binding.dom.codec.impl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
/**
* Abstract base for {@link DataObjectCodecContext} and {@link AugmentationCodecContext}. They share most of their
* mechanics, but notably:
*
* - DataObjectCodecContext has an exact DistinctNodeContainer and YangInstanceIdentifier mapping and can be the
* target of augmentations (i.e. can implement Augmentable contract)
* - AugmentationNodeContext has neither of those traits and really is just a filter of its parent
* DistinctNodeContainer
*
*
*
* Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy.
*
*
* While this class is public, it not part of API surface and is an implementation detail. The only reason for it being
* public is that it needs to be accessible by code generated at runtime.
*/
public abstract sealed class AbstractDataObjectCodecContext
extends CommonDataObjectCodecContext
permits AugmentationCodecContext, DataObjectCodecContext {
private final ImmutableMap, CommonDataObjectCodecPrototype>> byBindingArgClass;
private final ImmutableMap, CommonDataObjectCodecPrototype>> byStreamClass;
private final ImmutableMap byYang;
private final ImmutableMap leafChild;
AbstractDataObjectCodecContext(final CommonDataObjectCodecPrototype prototype,
final DataContainerAnalysis analysis) {
super(prototype);
byBindingArgClass = analysis.byBindingArgClass;
byStreamClass = analysis.byStreamClass;
byYang = analysis.byYang;
leafChild = analysis.leafNodes;
}
@Override
public final WithStatus getSchema() {
// FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
return (WithStatus) type().statement();
}
@Override
CommonDataObjectCodecPrototype> streamChildPrototype(final Class> childClass) {
return byStreamClass.get(childClass);
}
@Override
public final CommonDataObjectCodecContext, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
final List builder) {
final var argType = arg.getType();
final var context = childNonNull(pathChildPrototype(argType), argType,
"Class %s is not valid child of %s", argType, getBindingClass())
.getCodecContext();
if (context instanceof ChoiceCodecContext> choice) {
choice.addYangPathArgument(arg, builder);
final var caseType = arg.getCaseType();
final var type = arg.getType();
final DataContainerCodecContext, ?> caze;
if (caseType.isPresent()) {
// Non-ambiguous addressing this should not pose any problems
caze = choice.getStreamChild(caseType.orElseThrow());
} else {
caze = choice.getCaseByChildClass(type);
}
caze.addYangPathArgument(arg, builder);
return caze.bindingPathArgumentChild(arg, builder);
}
context.addYangPathArgument(arg, builder);
return context;
}
@Nullable CommonDataObjectCodecPrototype> pathChildPrototype(final @NonNull Class extends DataObject> argType) {
return byBindingArgClass.get(argType);
}
@Override
CodecContextSupplier yangChildSupplier(final NodeIdentifier arg) {
return byYang.get(arg);
}
final ValueNodeCodecContext getLeafChild(final String name) {
final ValueNodeCodecContext value = leafChild.get(name);
if (value == null) {
throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
}
return value;
}
final @NonNull ImmutableSet byYangKeySet() {
return byYang.keySet();
}
abstract @NonNull Map>, Augmentation>> getAllAugmentationsFrom(
DataContainerNode data);
}