Update binding-dom adaptation to remove AugmentationNode
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / AugmentableCodecDataObject.java
1 /*
2  * Copyright (c) 2019 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 static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableMap;
13 import java.lang.invoke.MethodHandles;
14 import java.lang.invoke.VarHandle;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.yangtools.yang.binding.Augmentable;
18 import org.opendaylight.yangtools.yang.binding.Augmentation;
19 import org.opendaylight.yangtools.yang.binding.DataObject;
20 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
22
23 /**
24  * A base class for {@link DataObject}s which are also {@link Augmentable}, backed by {@link DataObjectCodecContext}.
25  * While this class is public, it not part of API surface and is an implementation detail. The only reason for it being
26  * public is that it needs to be accessible by code generated at runtime.
27  *
28  * @param <T> DataObject type
29  */
30 public abstract class AugmentableCodecDataObject<T extends DataObject & Augmentable<T>>
31         extends CodecDataObject<T> implements Augmentable<T> {
32     private static final VarHandle CACHED_AUGMENTATIONS;
33
34     static {
35         try {
36             CACHED_AUGMENTATIONS = MethodHandles.lookup().findVarHandle(AugmentableCodecDataObject.class,
37                 "cachedAugmentations", ImmutableMap.class);
38         } catch (ReflectiveOperationException e) {
39             throw new ExceptionInInitializerError(e);
40         }
41     }
42
43     // Used via VarHandle
44     @SuppressWarnings("unused")
45     private volatile ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> cachedAugmentations;
46
47     protected AugmentableCodecDataObject(final AbstractDataObjectCodecContext<T, ?> context,
48             final DistinctNodeContainer<?, ?> data) {
49         super(context, data);
50     }
51
52     @SuppressWarnings("unchecked")
53     @Override
54     public final <A extends Augmentation<T>> @Nullable A augmentation(final Class<A> augmentationType) {
55         requireNonNull(augmentationType, "Supplied augmentation must not be null.");
56
57         final var aug = acquireAugmentations();
58         if (aug != null) {
59             return (A) aug.get(augmentationType);
60         }
61
62         @SuppressWarnings("rawtypes")
63         final var optAugCtx = codecContext().possibleStreamChild((Class) augmentationType);
64         if (optAugCtx.isPresent()) {
65             final var augCtx = (AugmentationNodeContext<A>) optAugCtx.orElseThrow();
66             // Due to binding specification not representing grouping instantiations we can end up having the same
67             // augmentation applied to a grouping multiple times. While these augmentations have the same shape, they
68             // are still represented by distinct binding classes and therefore we need to make sure the result matches
69             // the augmentation the user is requesting -- otherwise a strict receiver would end up with a cryptic
70             // ClassCastException.
71             if (augmentationType.isAssignableFrom(augCtx.getBindingClass())) {
72                 final var augObj = augCtx.filterFrom((DataContainerNode) codecData());
73                 if (augObj != null) {
74                     return augObj;
75                 }
76             }
77         }
78         return null;
79     }
80
81     @Override
82     public final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> augmentations() {
83         final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> local = acquireAugmentations();
84         return local != null ? local : loadAugmentations();
85     }
86
87     private ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> acquireAugmentations() {
88         return (ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>>) CACHED_AUGMENTATIONS.getAcquire(this);
89     }
90
91     @SuppressWarnings("unchecked")
92     private @NonNull ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> loadAugmentations() {
93         final var ret = ImmutableMap.copyOf(codecContext().getAllAugmentationsFrom(codecData()));
94         final Object witness = CACHED_AUGMENTATIONS.compareAndExchangeRelease(this, null, ret);
95         return witness == null ? ret : (ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>>) witness;
96     }
97 }