d0b87666a43a61509f3c6367f3380ffa4b825af9
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / AbstractDataObjectCodecContext.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
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;
15 import java.util.Map;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
19 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
20 import org.opendaylight.yangtools.yang.binding.Augmentation;
21 import org.opendaylight.yangtools.yang.binding.DataObject;
22 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
25 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
26 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
27
28 /**
29  * Abstract base for {@link DataObjectCodecContext} and {@link AugmentationCodecContext}. They share most of their
30  * mechanics, but notably:
31  * <ol>
32  *   <li>DataObjectCodecContext has an exact DistinctNodeContainer and YangInstanceIdentifier mapping and can be the
33  *       target of augmentations (i.e. can implement Augmentable contract)</li>
34  *   <li>AugmentationNodeContext has neither of those traits and really is just a filter of its parent
35  *       DistinctNodeContainer</li>
36  * </ol>
37  *
38  * <p>
39  * Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy.
40  *
41  * <p>
42  * While this class is public, it not part of API surface and is an implementation detail. The only reason for it being
43  * public is that it needs to be accessible by code generated at runtime.
44  */
45 public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
46         extends CommonDataObjectCodecContext<D, T>
47         permits AugmentationCodecContext, DataObjectCodecContext {
48     private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byBindingArgClass;
49     private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byStreamClass;
50     private final ImmutableMap<NodeIdentifier, CodecContextSupplier> byYang;
51     private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
52     private final MethodHandle proxyConstructor;
53
54     AbstractDataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype,
55             final CodecDataObjectAnalysis<T> analysis) {
56         super(prototype);
57         byBindingArgClass = analysis.byBindingArgClass;
58         byStreamClass = analysis.byStreamClass;
59         byYang = analysis.byYang;
60         leafChild = analysis.leafNodes;
61         proxyConstructor = analysis.proxyConstructor;
62     }
63
64     @Override
65     public final WithStatus getSchema() {
66         // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
67         return (WithStatus) type().statement();
68     }
69
70     @Override
71     CommonDataObjectCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
72         return byStreamClass.get(childClass);
73     }
74
75     @Override
76     public final CommonDataObjectCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
77             final List<PathArgument> builder) {
78         final var argType = arg.getType();
79         final var context = childNonNull(pathChildPrototype(argType), argType,
80             "Class %s is not valid child of %s", argType, getBindingClass())
81             .get();
82         if (context instanceof ChoiceCodecContext<?> choice) {
83             choice.addYangPathArgument(arg, builder);
84
85             final var caseType = arg.getCaseType();
86             final var type = arg.getType();
87             final DataContainerCodecContext<?, ?> caze;
88             if (caseType.isPresent()) {
89                 // Non-ambiguous addressing this should not pose any problems
90                 caze = choice.getStreamChild(caseType.orElseThrow());
91             } else {
92                 caze = choice.getCaseByChildClass(type);
93             }
94
95             caze.addYangPathArgument(arg, builder);
96             return caze.bindingPathArgumentChild(arg, builder);
97         }
98         context.addYangPathArgument(arg, builder);
99         return context;
100     }
101
102     @Nullable CommonDataObjectCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
103         return byBindingArgClass.get(argType);
104     }
105
106     @Override
107     CodecContextSupplier yangChildSupplier(final NodeIdentifier arg) {
108         return byYang.get(arg);
109     }
110
111     @SuppressWarnings("checkstyle:illegalCatch")
112     final @NonNull D createBindingProxy(final DataContainerNode node) {
113         try {
114             return (D) proxyConstructor.invokeExact(this, node);
115         } catch (final Throwable e) {
116             Throwables.throwIfUnchecked(e);
117             throw new IllegalStateException(e);
118         }
119     }
120
121     final ValueNodeCodecContext getLeafChild(final String name) {
122         final ValueNodeCodecContext value = leafChild.get(name);
123         if (value == null) {
124             throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
125         }
126         return value;
127     }
128
129     final @NonNull ImmutableSet<NodeIdentifier> byYangKeySet() {
130         return byYang.keySet();
131     }
132
133     abstract @NonNull Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
134         DataContainerNode data);
135 }