Clean up BindingAugmentationCodecTreeNode
[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 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;
30
31 /**
32  * Abstract base for {@link DataObjectCodecContext} and {@link AugmentationNodeContext}. They share most of their
33  * mechanics, but notably:
34  * <ol>
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>
39  * </ol>
40  *
41  * <p>
42  * Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy.
43  *
44  * <p>
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.
47  */
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<PathArgument, NodeContextSupplier> byYang;
53     private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
54     private final MethodHandle proxyConstructor;
55
56     AbstractDataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
57             final CodecDataObjectAnalysis<T> analysis) {
58         super(prototype);
59         byBindingArgClass = analysis.byBindingArgClass;
60         byStreamClass = analysis.byStreamClass;
61         byYang = analysis.byYang;
62         leafChild = analysis.leafNodes;
63         proxyConstructor = analysis.proxyConstructor;
64     }
65
66     @Override
67     public final WithStatus getSchema() {
68         // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
69         return (WithStatus) getType().statement();
70     }
71
72     @Override
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();
77     }
78
79     @SuppressWarnings("unchecked")
80     @Override
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());
86         }
87         return Optional.empty();
88     }
89
90     @Nullable DataContainerCodecPrototype<?> streamChildPrototype(final @NonNull Class<?> childClass) {
91         return byStreamClass.get(childClass);
92     }
93
94     @Override
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())
100             .get();
101         if (context instanceof ChoiceNodeCodecContext<?> choice) {
102             choice.addYangPathArgument(arg, builder);
103
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());
110             } else {
111                 caze = choice.getCaseByChildClass(type);
112             }
113
114             caze.addYangPathArgument(arg, builder);
115             return caze.bindingPathArgumentChild(arg, builder);
116         }
117         context.addYangPathArgument(arg, builder);
118         return context;
119     }
120
121     @Nullable DataContainerCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
122         return byBindingArgClass.get(argType);
123     }
124
125     @Override
126     public final NodeCodecContext yangPathArgumentChild(final PathArgument arg) {
127         final var lookup = arg instanceof NodeIdentifierWithPredicates ? new NodeIdentifier(arg.getNodeType()) : arg;
128         return childNonNull(yangChildSupplier(lookup), arg,
129             "Argument %s is not valid child of %s", arg, getSchema())
130             .get();
131     }
132
133     // FIXME: Never contains NodeIdentifierWithPredicates, what about NodeWithValue?
134     //        If it can't be here, it is always NodeIdentifier and we should specify that
135     @Nullable NodeContextSupplier yangChildSupplier(final @NonNull PathArgument arg) {
136         return byYang.get(arg);
137     }
138
139     @SuppressWarnings("checkstyle:illegalCatch")
140     final @NonNull D createBindingProxy(final DistinctNodeContainer<?, ?> node) {
141         try {
142             return (D) proxyConstructor.invokeExact(this, node);
143         } catch (final Throwable e) {
144             Throwables.throwIfUnchecked(e);
145             throw new IllegalStateException(e);
146         }
147     }
148
149     final ValueNodeCodecContext getLeafChild(final String name) {
150         final ValueNodeCodecContext value = leafChild.get(name);
151         if (value == null) {
152             throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
153         }
154         return value;
155     }
156
157     final @NonNull ImmutableSet<PathArgument> byYangKeySet() {
158         return byYang.keySet();
159     }
160
161     abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
162         DistinctNodeContainer<PathArgument, NormalizedNode> data);
163 }