Hide DataContainerCodecContext.getType()
[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.NodeIdentifierWithPredicates;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
26 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
27 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
28
29 /**
30  * Abstract base for {@link DataObjectCodecContext} and {@link AugmentationCodecContext}. They share most of their
31  * mechanics, but notably:
32  * <ol>
33  *   <li>DataObjectCodecContext has an exact DistinctNodeContainer and YangInstanceIdentifier mapping and can be the
34  *       target of augmentations (i.e. can implement Augmentable contract)</li>
35  *   <li>AugmentationNodeContext has neither of those traits and really is just a filter of its parent
36  *       DistinctNodeContainer</li>
37  * </ol>
38  *
39  * <p>
40  * Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy.
41  *
42  * <p>
43  * While this class is public, it not part of API surface and is an implementation detail. The only reason for it being
44  * public is that it needs to be accessible by code generated at runtime.
45  */
46 public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
47         extends DataContainerCodecContext<D, T>
48         permits AugmentationCodecContext, DataObjectCodecContext {
49     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
50     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
51     private final ImmutableMap<NodeIdentifier, CodecContextSupplier> byYang;
52     private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
53     private final MethodHandle proxyConstructor;
54
55     AbstractDataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
56             final CodecDataObjectAnalysis<T> analysis) {
57         super(prototype);
58         byBindingArgClass = analysis.byBindingArgClass;
59         byStreamClass = analysis.byStreamClass;
60         byYang = analysis.byYang;
61         leafChild = analysis.leafNodes;
62         proxyConstructor = analysis.proxyConstructor;
63     }
64
65     @Override
66     public final WithStatus getSchema() {
67         // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
68         return (WithStatus) type().statement();
69     }
70
71     @Override
72     public final <C extends DataObject> DataContainerCodecContext<C, ?> getStreamChild(final Class<C> childClass) {
73         return childNonNull(streamChild(childClass), childClass,
74             "Child %s is not valid child of %s", getBindingClass(), childClass);
75     }
76
77     @SuppressWarnings("unchecked")
78     @Override
79     public final <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
80         final var childProto = streamChildPrototype(childClass);
81         return childProto == null ? null : (DataContainerCodecContext<C, ?>) childProto.get();
82     }
83
84     @Nullable DataContainerCodecPrototype<?> streamChildPrototype(final @NonNull Class<?> childClass) {
85         return byStreamClass.get(childClass);
86     }
87
88     @Override
89     public final DataContainerCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
90             final List<PathArgument> builder) {
91         final var argType = arg.getType();
92         final var context = childNonNull(pathChildPrototype(argType), argType,
93             "Class %s is not valid child of %s", argType, getBindingClass())
94             .get();
95         if (context instanceof ChoiceCodecContext<?> choice) {
96             choice.addYangPathArgument(arg, builder);
97
98             final var caseType = arg.getCaseType();
99             final var type = arg.getType();
100             final DataContainerCodecContext<?, ?> caze;
101             if (caseType.isPresent()) {
102                 // Non-ambiguous addressing this should not pose any problems
103                 caze = choice.getStreamChild(caseType.orElseThrow());
104             } else {
105                 caze = choice.getCaseByChildClass(type);
106             }
107
108             caze.addYangPathArgument(arg, builder);
109             return caze.bindingPathArgumentChild(arg, builder);
110         }
111         context.addYangPathArgument(arg, builder);
112         return context;
113     }
114
115     @Nullable DataContainerCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
116         return byBindingArgClass.get(argType);
117     }
118
119     @Override
120     public final CodecContext yangPathArgumentChild(final PathArgument arg) {
121         CodecContextSupplier supplier;
122         if (arg instanceof NodeIdentifier nodeId) {
123             supplier = yangChildSupplier(nodeId);
124         } else if (arg instanceof NodeIdentifierWithPredicates nip) {
125             supplier = yangChildSupplier(new NodeIdentifier(nip.getNodeType()));
126         } else {
127             supplier = null;
128         }
129         return childNonNull(supplier, arg, "Argument %s is not valid child of %s", arg, getSchema()).get();
130     }
131
132     @Nullable CodecContextSupplier yangChildSupplier(final @NonNull NodeIdentifier arg) {
133         return byYang.get(arg);
134     }
135
136     @SuppressWarnings("checkstyle:illegalCatch")
137     final @NonNull D createBindingProxy(final DataContainerNode node) {
138         try {
139             return (D) proxyConstructor.invokeExact(this, node);
140         } catch (final Throwable e) {
141             Throwables.throwIfUnchecked(e);
142             throw new IllegalStateException(e);
143         }
144     }
145
146     final ValueNodeCodecContext getLeafChild(final String name) {
147         final ValueNodeCodecContext value = leafChild.get(name);
148         if (value == null) {
149             throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
150         }
151         return value;
152     }
153
154     final @NonNull ImmutableSet<NodeIdentifier> byYangKeySet() {
155         return byYang.keySet();
156     }
157
158     abstract @NonNull Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
159         DataContainerNode data);
160 }