Remove AugmentationReader
[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.base.MoreObjects.ToStringHelper;
13 import com.google.common.collect.ImmutableMap;
14 import java.lang.invoke.MethodHandles;
15 import java.lang.invoke.VarHandle;
16 import java.util.Optional;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
19 import org.opendaylight.yangtools.yang.binding.Augmentable;
20 import org.opendaylight.yangtools.yang.binding.Augmentation;
21 import org.opendaylight.yangtools.yang.binding.AugmentationHolder;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
25
26 /**
27  * A base class for {@link DataObject}s which are also {@link Augmentable}, backed by {@link DataObjectCodecContext}.
28  * While this class is public, it not part of API surface and is an implementation detail. The only reason for it being
29  * public is that it needs to be accessible by code generated at runtime.
30  *
31  * @param <T> DataObject type
32  */
33 public abstract class AugmentableCodecDataObject<T extends DataObject & Augmentable<T>>
34         extends CodecDataObject<T> implements Augmentable<T>, AugmentationHolder<T> {
35     private static final VarHandle CACHED_AUGMENTATIONS;
36
37     static {
38         try {
39             CACHED_AUGMENTATIONS = MethodHandles.lookup().findVarHandle(AugmentableCodecDataObject.class,
40                 "cachedAugmentations", ImmutableMap.class);
41         } catch (ReflectiveOperationException e) {
42             throw new ExceptionInInitializerError(e);
43         }
44     }
45
46     // Used via VarHandle
47     @SuppressWarnings("unused")
48     private volatile ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> cachedAugmentations;
49
50     protected AugmentableCodecDataObject(final DataObjectCodecContext<T, ?> context,
51             final NormalizedNodeContainer<?, ?, ?> data) {
52         super(context, data);
53     }
54
55     @SuppressWarnings("unchecked")
56     @Override
57     public final <A extends Augmentation<T>> @Nullable A augmentation(final Class<A> augmentationType) {
58         requireNonNull(augmentationType, "Supplied augmentation must not be null.");
59
60         final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> aug = acquireAugmentations();
61         if (aug != null) {
62             return (A) aug.get(augmentationType);
63         }
64
65         @SuppressWarnings("rawtypes")
66         final Optional<DataContainerCodecContext<?, ?>> optAugCtx = codecContext().possibleStreamChild(
67             (Class) augmentationType);
68         if (optAugCtx.isPresent()) {
69             final DataContainerCodecContext<?, ?> augCtx = optAugCtx.get();
70             // Due to binding specification not representing grouping instantiations we can end up having the same
71             // augmentation applied to a grouping multiple times. While these augmentations have the same shape, they
72             // are still represented by distinct binding classes and therefore we need to make sure the result matches
73             // the augmentation the user is requesting -- otherwise a strict receiver would end up with a cryptic
74             // ClassCastException.
75             if (augmentationType.isAssignableFrom(augCtx.getBindingClass())) {
76                 final Optional<NormalizedNode<?, ?>> augData = codecData().getChild(augCtx.getDomPathArgument());
77                 if (augData.isPresent()) {
78                     return (A) augCtx.deserialize(augData.get());
79                 }
80             }
81         }
82         return null;
83     }
84
85     @Override
86     public final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> augmentations() {
87         final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> local = acquireAugmentations();
88         return local != null ? local : loadAugmentations();
89     }
90
91     @Override
92     final int codecAugmentedHashCode() {
93         return 31 * super.codecAugmentedHashCode() + augmentations().hashCode();
94     }
95
96     @Override
97     final boolean codecAugmentedEquals(final T other) {
98         return super.codecAugmentedEquals(other) && augmentations().equals(BindingReflections.getAugmentations(other));
99     }
100
101     @Override
102     final ToStringHelper codecAugmentedFillToString(final ToStringHelper helper) {
103         return super.codecAugmentedFillToString(helper).add("augmentation", augmentations().values());
104     }
105
106     private ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> acquireAugmentations() {
107         return (ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>>) CACHED_AUGMENTATIONS.getAcquire(this);
108     }
109
110     @SuppressWarnings("unchecked")
111     private ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> loadAugmentations() {
112         final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> ret = ImmutableMap.copyOf(
113             codecContext().getAllAugmentationsFrom(codecData()));
114         final Object witness = CACHED_AUGMENTATIONS.compareAndExchangeRelease(this, null, ret);
115         return witness == null ? ret : (ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>>) witness;
116     }
117 }