2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
10 import com.google.common.base.Throwables;
11 import com.google.common.collect.ImmutableMap;
12 import com.google.common.collect.ImmutableSet;
13 import java.lang.invoke.MethodHandle;
14 import java.util.List;
16 import java.util.Optional;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
20 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
21 import org.opendaylight.yangtools.yang.binding.Augmentation;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
28 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
29 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
32 * Abstract base for {@link DataObjectCodecContext} and {@link AugmentationNodeContext}. They share most of their
33 * mechanics, but notably:
35 * <li>DataObjectCodecContext has an exact DistinctNodeContainer and YangInstanceIdentifier mapping and can be the
36 * target of augmentations (i.e. can implement Augmentable contract)</li>
37 * <li>AugmentationNodeContext has neither of those traits and really is just a filter of its parent
38 * DistinctNodeContainer</li>
42 * Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy.
45 * While this class is public, it not part of API surface and is an implementation detail. The only reason for it being
46 * public is that it needs to be accessible by code generated at runtime.
48 public abstract class AbstractDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
49 extends DataContainerCodecContext<D, T> {
50 private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
51 private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
52 private final ImmutableMap<NodeIdentifier, NodeContextSupplier> byYang;
53 private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
54 private final MethodHandle proxyConstructor;
56 AbstractDataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
57 final CodecDataObjectAnalysis<T> analysis) {
59 byBindingArgClass = analysis.byBindingArgClass;
60 byStreamClass = analysis.byStreamClass;
61 byYang = analysis.byYang;
62 leafChild = analysis.leafNodes;
63 proxyConstructor = analysis.proxyConstructor;
67 public final WithStatus getSchema() {
68 // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
69 return (WithStatus) getType().statement();
73 @SuppressWarnings("unchecked")
74 public final <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
75 return (DataContainerCodecContext<C, ?>) childNonNull(streamChildPrototype(childClass), childClass,
76 "Child %s is not valid child of %s", getBindingClass(), childClass).get();
79 @SuppressWarnings("unchecked")
81 public final <C extends DataObject> Optional<DataContainerCodecContext<C, ?>> possibleStreamChild(
82 final Class<C> childClass) {
83 final var childProto = streamChildPrototype(childClass);
84 if (childProto != null) {
85 return Optional.of((DataContainerCodecContext<C, ?>) childProto.get());
87 return Optional.empty();
90 @Nullable DataContainerCodecPrototype<?> streamChildPrototype(final @NonNull Class<?> childClass) {
91 return byStreamClass.get(childClass);
95 public final DataContainerCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
96 final List<PathArgument> builder) {
97 final var argType = arg.getType();
98 final var context = childNonNull(pathChildPrototype(argType), argType,
99 "Class %s is not valid child of %s", argType, getBindingClass())
101 if (context instanceof ChoiceNodeCodecContext<?> choice) {
102 choice.addYangPathArgument(arg, builder);
104 final var caseType = arg.getCaseType();
105 final var type = arg.getType();
106 final DataContainerCodecContext<?, ?> caze;
107 if (caseType.isPresent()) {
108 // Non-ambiguous addressing this should not pose any problems
109 caze = choice.streamChild(caseType.orElseThrow());
111 caze = choice.getCaseByChildClass(type);
114 caze.addYangPathArgument(arg, builder);
115 return caze.bindingPathArgumentChild(arg, builder);
117 context.addYangPathArgument(arg, builder);
121 @Nullable DataContainerCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
122 return byBindingArgClass.get(argType);
126 public final NodeCodecContext yangPathArgumentChild(final PathArgument arg) {
127 NodeContextSupplier supplier;
128 if (arg instanceof NodeIdentifier nodeId) {
129 supplier = yangChildSupplier(nodeId);
130 } else if (arg instanceof NodeIdentifierWithPredicates nip) {
131 supplier = yangChildSupplier(new NodeIdentifier(nip.getNodeType()));
135 return childNonNull(supplier, arg, "Argument %s is not valid child of %s", arg, getSchema()).get();
138 @Nullable NodeContextSupplier yangChildSupplier(final @NonNull NodeIdentifier arg) {
139 return byYang.get(arg);
142 @SuppressWarnings("checkstyle:illegalCatch")
143 final @NonNull D createBindingProxy(final DistinctNodeContainer<?, ?> node) {
145 return (D) proxyConstructor.invokeExact(this, node);
146 } catch (final Throwable e) {
147 Throwables.throwIfUnchecked(e);
148 throw new IllegalStateException(e);
152 final ValueNodeCodecContext getLeafChild(final String name) {
153 final ValueNodeCodecContext value = leafChild.get(name);
155 throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
160 final @NonNull ImmutableSet<NodeIdentifier> byYangKeySet() {
161 return byYang.keySet();
164 abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
165 DistinctNodeContainer<PathArgument, NormalizedNode> data);