Move BindingReflections.isSubstitutionFor()
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / DataObjectCodecContext.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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 static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12 import static com.google.common.base.Verify.verifyNotNull;
13
14 import com.google.common.annotations.Beta;
15 import com.google.common.base.Throwables;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableMap.Builder;
18 import com.google.common.collect.ImmutableSet;
19 import java.lang.invoke.MethodHandle;
20 import java.lang.invoke.MethodHandles;
21 import java.lang.invoke.MethodType;
22 import java.lang.invoke.VarHandle;
23 import java.lang.reflect.Method;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Optional;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
32 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
33 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
34 import org.opendaylight.mdsal.binding.model.api.Type;
35 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
36 import org.opendaylight.mdsal.binding.runtime.api.AugmentableRuntimeType;
37 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
38 import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
39 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
40 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
41 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
42 import org.opendaylight.yangtools.yang.binding.Augmentable;
43 import org.opendaylight.yangtools.yang.binding.Augmentation;
44 import org.opendaylight.yangtools.yang.binding.DataContainer;
45 import org.opendaylight.yangtools.yang.binding.DataObject;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
48 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
52 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
53 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
54 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
55 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
57 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
58 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
59 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
60 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 /**
65  * This class is an implementation detail. It is public only due to technical reasons and may change at any time.
66  */
67 @Beta
68 public abstract class DataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
69         extends DataContainerCodecContext<D, T> {
70     private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class);
71     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class,
72         DataObjectCodecContext.class, DistinctNodeContainer.class);
73     private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class,
74         DataObjectCodecContext.class, DistinctNodeContainer.class);
75     private static final VarHandle MISMATCHED_AUGMENTED;
76
77     static {
78         try {
79             MISMATCHED_AUGMENTED = MethodHandles.lookup().findVarHandle(DataObjectCodecContext.class,
80                 "mismatchedAugmented", ImmutableMap.class);
81         } catch (NoSuchFieldException | IllegalAccessException e) {
82             throw new ExceptionInInitializerError(e);
83         }
84     }
85
86     private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
87     private final ImmutableMap<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYang;
88     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
89     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
90     private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> augmentationByYang;
91     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> augmentationByStream;
92     private final @NonNull Class<? extends CodecDataObject<?>> generatedClass;
93     private final MethodHandle proxyConstructor;
94
95     // Note this the content of this field depends only of invariants expressed as this class's fields or
96     // BindingRuntimeContext. It is only accessed via MISMATCHED_AUGMENTED above.
97     @SuppressWarnings("unused")
98     private volatile ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> mismatchedAugmented = ImmutableMap.of();
99
100     DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype) {
101         this(prototype, null);
102     }
103
104     DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype, final Method keyMethod) {
105         super(prototype);
106
107         final Class<D> bindingClass = getBindingClass();
108
109         final ImmutableMap<Method, ValueNodeCodecContext> tmpLeaves = factory().getLeafNodes(bindingClass,
110             getType().statement());
111         final Map<Class<? extends DataContainer>, Method> clsToMethod = getChildrenClassToMethod(bindingClass);
112
113         final Map<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYangBuilder = new HashMap<>();
114         final Map<Class<?>, DataContainerCodecPrototype<?>> byStreamClassBuilder = new HashMap<>();
115         final Map<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClassBuilder = new HashMap<>();
116
117         // Adds leaves to mapping
118         final Builder<String, ValueNodeCodecContext> leafChildBuilder =
119                 ImmutableMap.builderWithExpectedSize(tmpLeaves.size());
120         for (final ValueNodeCodecContext leaf : tmpLeaves.values()) {
121             leafChildBuilder.put(leaf.getSchema().getQName().getLocalName(), leaf);
122             byYangBuilder.put(leaf.getDomPathArgument(), leaf);
123         }
124         this.leafChild = leafChildBuilder.build();
125
126         final Map<Class<?>, PropertyInfo> daoProperties = new HashMap<>();
127         for (final Entry<Class<? extends DataContainer>, Method> childDataObj : clsToMethod.entrySet()) {
128             final Method method = childDataObj.getValue();
129             verify(!method.isDefault(), "Unexpected default method %s in %s", method, bindingClass);
130
131             final Class<? extends DataContainer> retClass = childDataObj.getKey();
132             if (OpaqueObject.class.isAssignableFrom(retClass)) {
133                 // Filter OpaqueObjects, they are not containers
134                 continue;
135             }
136
137             // Record getter method
138             daoProperties.put(retClass, new PropertyInfo.Getter(method));
139
140             final DataContainerCodecPrototype<?> childProto = loadChildPrototype(retClass);
141             byStreamClassBuilder.put(childProto.getBindingClass(), childProto);
142             byYangBuilder.put(childProto.getYangArg(), childProto);
143
144             // FIXME: It really feels like we should be specializing DataContainerCodecPrototype so as to ditch
145             //        createInstance() and then we could do an instanceof check instead.
146             if (childProto.getType() instanceof ChoiceRuntimeType) {
147                 final ChoiceNodeCodecContext<?> choice = (ChoiceNodeCodecContext<?>) childProto.get();
148                 for (final Class<?> cazeChild : choice.getCaseChildrenClasses()) {
149                     byBindingArgClassBuilder.put(cazeChild, childProto);
150                 }
151             }
152         }
153
154         // Find all non-default nonnullFoo() methods and update the corresponding property info
155         for (var entry : getChildrenClassToNonnullMethod(bindingClass).entrySet()) {
156             final var method = entry.getValue();
157             if (!method.isDefault()) {
158                 daoProperties.compute(entry.getKey(), (key, value) -> new PropertyInfo.GetterAndNonnull(
159                     verifyNotNull(value, "No getter for %s", key).getterMethod(), method));
160             }
161         }
162
163         this.byYang = ImmutableMap.copyOf(byYangBuilder);
164         this.byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder);
165
166         // Slight footprint optimization: we do not want to copy byStreamClass, as that would force its entrySet view
167         // to be instantiated. Furthermore the two maps can easily end up being equal -- hence we can reuse
168         // byStreamClass for the purposes of both.
169         byBindingArgClassBuilder.putAll(byStreamClassBuilder);
170         this.byBindingArgClass = byStreamClassBuilder.equals(byBindingArgClassBuilder) ? this.byStreamClass
171                 : ImmutableMap.copyOf(byBindingArgClassBuilder);
172
173         final List<AugmentRuntimeType> possibleAugmentations;
174         if (Augmentable.class.isAssignableFrom(bindingClass)) {
175             // Verify we have the appropriate backing runtimeType
176             final var type = getType();
177             verify(type instanceof AugmentableRuntimeType, "Unexpected type %s backing augmenable %s", type,
178                 bindingClass);
179             possibleAugmentations = ((AugmentableRuntimeType) type).augments();
180             generatedClass = CodecDataObjectGenerator.generateAugmentable(prototype.getFactory().getLoader(),
181                 bindingClass, tmpLeaves, daoProperties, keyMethod);
182         } else {
183             possibleAugmentations = List.of();
184             generatedClass = CodecDataObjectGenerator.generate(prototype.getFactory().getLoader(), bindingClass,
185                 tmpLeaves, daoProperties, keyMethod);
186         }
187
188         // Iterate over all possible augmentations, indexing them as needed
189         final Map<PathArgument, DataContainerCodecPrototype<?>> augByYang = new HashMap<>();
190         final Map<Class<?>, DataContainerCodecPrototype<?>> augByStream = new HashMap<>();
191         for (final AugmentRuntimeType augment : possibleAugmentations) {
192             final DataContainerCodecPrototype<?> augProto = loadAugmentPrototype(augment);
193             if (augProto != null) {
194                 final PathArgument augYangArg = augProto.getYangArg();
195                 if (augByYang.putIfAbsent(augYangArg, augProto) == null) {
196                     LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this);
197                 }
198                 final Class<?> augBindingClass = augProto.getBindingClass();
199                 if (augByStream.putIfAbsent(augBindingClass, augProto) == null) {
200                     LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this);
201                 }
202             }
203         }
204         augmentationByYang = ImmutableMap.copyOf(augByYang);
205         augmentationByStream = ImmutableMap.copyOf(augByStream);
206
207         final MethodHandle ctor;
208         try {
209             ctor = MethodHandles.publicLookup().findConstructor(generatedClass, CONSTRUCTOR_TYPE);
210         } catch (NoSuchMethodException | IllegalAccessException e) {
211             throw new LinkageError("Failed to find contructor for class " + generatedClass, e);
212         }
213
214         proxyConstructor = ctor.asType(DATAOBJECT_TYPE);
215     }
216
217     @Override
218     public final WithStatus getSchema() {
219         // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
220         return (WithStatus) getType().statement();
221     }
222
223     @Override
224     @SuppressWarnings("unchecked")
225     public <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
226         return (DataContainerCodecContext<C, ?>) childNonNull(streamChildPrototype(childClass), childClass,
227             "Child %s is not valid child of %s", getBindingClass(), childClass).get();
228     }
229
230     private DataContainerCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
231         final DataContainerCodecPrototype<?> childProto = byStreamClass.get(childClass);
232         if (childProto != null) {
233             return childProto;
234         }
235         if (Augmentation.class.isAssignableFrom(childClass)) {
236             return augmentationByClass(childClass);
237         }
238         return null;
239     }
240
241     @SuppressWarnings("unchecked")
242     @Override
243     public <C extends DataObject> Optional<DataContainerCodecContext<C, ?>> possibleStreamChild(
244             final Class<C> childClass) {
245         final DataContainerCodecPrototype<?> childProto = streamChildPrototype(childClass);
246         if (childProto != null) {
247             return Optional.of((DataContainerCodecContext<C, ?>) childProto.get());
248         }
249         return Optional.empty();
250     }
251
252     @Override
253     public DataContainerCodecContext<?,?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
254             final List<YangInstanceIdentifier.PathArgument> builder) {
255
256         final Class<? extends DataObject> argType = arg.getType();
257         DataContainerCodecPrototype<?> ctxProto = byBindingArgClass.get(argType);
258         if (ctxProto == null && Augmentation.class.isAssignableFrom(argType)) {
259             ctxProto = augmentationByClass(argType);
260         }
261         final DataContainerCodecContext<?, ?> context = childNonNull(ctxProto, argType,
262             "Class %s is not valid child of %s", argType, getBindingClass()).get();
263         if (context instanceof ChoiceNodeCodecContext) {
264             final ChoiceNodeCodecContext<?> choice = (ChoiceNodeCodecContext<?>) context;
265             choice.addYangPathArgument(arg, builder);
266
267             final Optional<? extends Class<? extends DataObject>> caseType = arg.getCaseType();
268             final Class<? extends DataObject> type = arg.getType();
269             final DataContainerCodecContext<?, ?> caze;
270             if (caseType.isPresent()) {
271                 // Non-ambiguous addressing this should not pose any problems
272                 caze = choice.streamChild(caseType.get());
273             } else {
274                 caze = choice.getCaseByChildClass(type);
275             }
276
277             caze.addYangPathArgument(arg, builder);
278             return caze.bindingPathArgumentChild(arg, builder);
279         }
280         context.addYangPathArgument(arg, builder);
281         return context;
282     }
283
284     @Override
285     public NodeCodecContext yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
286         final NodeContextSupplier childSupplier;
287         if (arg instanceof NodeIdentifierWithPredicates) {
288             childSupplier = byYang.get(new NodeIdentifier(arg.getNodeType()));
289         } else if (arg instanceof AugmentationIdentifier) {
290             childSupplier = augmentationByYang.get(arg);
291         } else {
292             childSupplier = byYang.get(arg);
293         }
294
295         return childNonNull(childSupplier, arg, "Argument %s is not valid child of %s", arg, getSchema()).get();
296     }
297
298     protected final ValueNodeCodecContext getLeafChild(final String name) {
299         final ValueNodeCodecContext value = leafChild.get(name);
300         if (value == null) {
301             throw IncorrectNestingException.create("Leaf %s is not valid for %s", name, getBindingClass());
302         }
303         return value;
304     }
305
306     private DataContainerCodecPrototype<?> loadChildPrototype(final Class<? extends DataContainer> childClass) {
307         final var type = getType();
308         final var child = childNonNull(type.bindingChild(JavaTypeName.create(childClass)), childClass,
309             "Node %s does not have child named %s", type, childClass);
310
311         return DataContainerCodecPrototype.from(createBindingArg(childClass, child.statement()),
312             (CompositeRuntimeType) child, factory());
313     }
314
315     // FIXME: MDSAL-697: move this method into BindingRuntimeContext
316     //                   This method is only called from loadChildPrototype() and exists only to be overridden by
317     //                   CaseNodeCodecContext. Since we are providing childClass and our schema to BindingRuntimeContext
318     //                   and receiving childSchema from it via findChildSchemaDefinition, we should be able to receive
319     //                   the equivalent of Map.Entry<Item, DataSchemaNode>, along with the override we create here. One
320     //                   more input we may need to provide is our bindingClass().
321     @SuppressWarnings("unchecked")
322     Item<?> createBindingArg(final Class<?> childClass, final EffectiveStatement<?, ?> childSchema) {
323         return Item.of((Class<? extends DataObject>) childClass);
324     }
325
326     private @Nullable DataContainerCodecPrototype<?> augmentationByClass(final @NonNull Class<?> childClass) {
327         final DataContainerCodecPrototype<?> childProto = augmentationByStream.get(childClass);
328         return childProto != null ? childProto : mismatchedAugmentationByClass(childClass);
329     }
330
331     private @Nullable DataContainerCodecPrototype<?> mismatchedAugmentationByClass(final @NonNull Class<?> childClass) {
332         /*
333          * It is potentially mismatched valid augmentation - we look up equivalent augmentation using reflection
334          * and walk all stream child and compare augmentations classes if they are equivalent. When we find a match
335          * we'll cache it so we do not need to perform reflection operations again.
336          */
337         final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> local =
338                 (ImmutableMap<Class<?>, DataContainerCodecPrototype<?>>) MISMATCHED_AUGMENTED.getAcquire(this);
339         final DataContainerCodecPrototype<?> mismatched = local.get(childClass);
340         return mismatched != null ? mismatched : loadMismatchedAugmentation(local, childClass);
341
342     }
343
344     private @Nullable DataContainerCodecPrototype<?> loadMismatchedAugmentation(
345             final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> oldMismatched,
346             final @NonNull Class<?> childClass) {
347         @SuppressWarnings("rawtypes")
348         final Class<?> augTarget = BindingReflections.findAugmentationTarget((Class) childClass);
349         // Do not bother with proposals which are not augmentations of our class, or do not match what the runtime
350         // context would load.
351         if (getBindingClass().equals(augTarget) && belongsToRuntimeContext(childClass)) {
352             for (final DataContainerCodecPrototype<?> realChild : augmentationByStream.values()) {
353                 if (Augmentation.class.isAssignableFrom(realChild.getBindingClass())
354                         && isSubstitutionFor(childClass, realChild.getBindingClass())) {
355                     return cacheMismatched(oldMismatched, childClass, realChild);
356                 }
357             }
358         }
359         LOG.trace("Failed to resolve {} as a valid augmentation in {}", childClass, this);
360         return null;
361     }
362
363     private @NonNull DataContainerCodecPrototype<?> cacheMismatched(
364             final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> oldMismatched,
365             final @NonNull Class<?> childClass, final @NonNull DataContainerCodecPrototype<?> prototype) {
366
367         ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> expected = oldMismatched;
368         while (true) {
369             final Map<Class<?>, DataContainerCodecPrototype<?>> newMismatched =
370                     ImmutableMap.<Class<?>, DataContainerCodecPrototype<?>>builderWithExpectedSize(expected.size() + 1)
371                         .putAll(expected)
372                         .put(childClass, prototype)
373                         .build();
374
375             final var witness = (ImmutableMap<Class<?>, DataContainerCodecPrototype<?>>)
376                 MISMATCHED_AUGMENTED.compareAndExchangeRelease(this, expected, newMismatched);
377             if (witness == expected) {
378                 LOG.trace("Cached mismatched augmentation {} -> {} in {}", childClass, prototype, this);
379                 return prototype;
380             }
381
382             expected = witness;
383             final DataContainerCodecPrototype<?> existing = expected.get(childClass);
384             if (existing != null) {
385                 LOG.trace("Using raced mismatched augmentation {} -> {} in {}", childClass, existing, this);
386                 return existing;
387             }
388         }
389     }
390
391     private boolean belongsToRuntimeContext(final Class<?> cls) {
392         final BindingRuntimeContext ctx = factory().getRuntimeContext();
393         final Class<?> loaded;
394         try {
395             loaded = ctx.loadClass(Type.of(cls));
396         } catch (ClassNotFoundException e) {
397             LOG.debug("Proposed {} cannot be loaded in {}", cls, ctx, e);
398             return false;
399         }
400         return cls.equals(loaded);
401     }
402
403     private @Nullable DataContainerCodecPrototype<?> loadAugmentPrototype(final AugmentRuntimeType augment) {
404         // FIXME: in face of deviations this code should be looking at declared view, i.e. all possibilities at augment
405         //        declaration site
406         final var possibleChildren = augment.statement()
407             .streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class)
408             .map(stmt -> (QName) stmt.argument())
409             .collect(ImmutableSet.toImmutableSet());
410         if (possibleChildren.isEmpty()) {
411             return null;
412         }
413
414         final var factory = factory();
415         final GeneratedType javaType = augment.javaType();
416         final Class<? extends Augmentation<?>> augClass;
417         try {
418             augClass = factory.getRuntimeContext().loadClass(javaType);
419         } catch (final ClassNotFoundException e) {
420             throw new IllegalStateException(
421                 "RuntimeContext references type " + javaType + " but failed to load its class", e);
422         }
423
424         return DataContainerCodecPrototype.from(augClass, new AugmentationIdentifier(possibleChildren), augment,
425             factory);
426     }
427
428     @SuppressWarnings("checkstyle:illegalCatch")
429     protected final @NonNull D createBindingProxy(final DistinctNodeContainer<?, ?> node) {
430         try {
431             return (D) proxyConstructor.invokeExact(this, node);
432         } catch (final Throwable e) {
433             Throwables.throwIfUnchecked(e);
434             throw new IllegalStateException(e);
435         }
436     }
437
438     @SuppressWarnings("unchecked")
439     Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
440             final DistinctNodeContainer<PathArgument, NormalizedNode> data) {
441
442         @SuppressWarnings("rawtypes")
443         final Map map = new HashMap<>();
444
445         for (final NormalizedNode childValue : data.body()) {
446             if (childValue instanceof AugmentationNode augDomNode) {
447                 final DataContainerCodecPrototype<?> codecProto = augmentationByYang.get(augDomNode.getIdentifier());
448                 if (codecProto != null) {
449                     final DataContainerCodecContext<?, ?> codec = codecProto.get();
450                     map.put(codec.getBindingClass(), codec.deserializeObject(augDomNode));
451                 }
452             }
453         }
454         for (final DataContainerCodecPrototype<?> value : augmentationByStream.values()) {
455             final var augClass = value.getBindingClass();
456             // Do not perform duplicate deserialization if we have already created the corresponding augmentation
457             // and validate whether the proposed augmentation is valid ion this instantiation context.
458             if (!map.containsKey(augClass)
459                 && ((AugmentableRuntimeType) getType()).augments().contains(value.getType())) {
460                 final NormalizedNode augData = data.childByArg(value.getYangArg());
461                 if (augData != null) {
462                     // ... make sure we do not replace an e
463                     map.putIfAbsent(augClass, value.get().deserializeObject(augData));
464                 }
465             }
466         }
467         return map;
468     }
469
470     final @NonNull Class<? extends CodecDataObject<?>> generatedClass() {
471         return generatedClass;
472     }
473
474     @Override
475     public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
476         checkArgument(getDomPathArgument().equals(arg));
477         return bindingArg();
478     }
479
480     @Override
481     public YangInstanceIdentifier.PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
482         checkArgument(bindingArg().equals(arg));
483         return getDomPathArgument();
484     }
485
486     /**
487      * Scans supplied class and returns an iterable of all data children classes.
488      *
489      * @param type YANG Modeled Entity derived from DataContainer
490      * @return Iterable of all data children, which have YANG modeled entity
491      */
492     // FIXME: MDSAL-780: replace use of this method
493     private static Map<Class<? extends DataContainer>, Method> getChildrenClassToMethod(final Class<?> type) {
494         return getChildClassToMethod(type, BindingMapping.GETTER_PREFIX);
495     }
496
497     // FIXME: MDSAL-780: replace use of this method
498     private static Map<Class<? extends DataContainer>, Method> getChildrenClassToNonnullMethod(final Class<?> type) {
499         return getChildClassToMethod(type, BindingMapping.NONNULL_PREFIX);
500     }
501
502     // FIXME: MDSAL-780: replace use of this method
503     private static Map<Class<? extends DataContainer>, Method> getChildClassToMethod(final Class<?> type,
504             final String prefix) {
505         checkArgument(type != null, "Target type must not be null");
506         checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type %s must be derived from DataContainer",
507             type);
508         final var ret = new HashMap<Class<? extends DataContainer>, Method>();
509         for (Method method : type.getMethods()) {
510             getYangModeledReturnType(method, prefix).ifPresent(entity -> ret.put(entity, method));
511         }
512         return ret;
513     }
514 }