2 * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.MoreObjects.ToStringHelper;
15 import java.util.Optional;
16 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.yangtools.yang.binding.DataObject;
20 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
25 * A base class for {@link DataObject}s backed by {@link DataObjectCodecContext}. While this class is public, it not
26 * part of API surface and is an implementation detail. The only reason for it being public is that it needs to be
27 * accessible by code generated at runtime.
29 * @param <T> DataObject type
31 public abstract class CodecDataObject<T extends DataObject> implements DataObject {
32 private static final @NonNull Object NULL_VALUE = new Object();
34 @SuppressWarnings("rawtypes")
35 private final @NonNull NormalizedNodeContainer data;
37 private volatile Integer cachedHashcode = null;
39 public CodecDataObject(final NormalizedNodeContainer<?, ?, ?> data) {
40 this.data = requireNonNull(data, "Data must not be null");
44 public final int hashCode() {
45 final Integer cached = cachedHashcode;
50 final int result = codecAugmentedHashCode();
51 cachedHashcode = result;
56 public final boolean equals(final Object obj) {
60 final Class<? extends DataObject> iface = implementedInterface();
61 if (!iface.isInstance(obj)) {
64 @SuppressWarnings("unchecked")
65 final T other = (T) iface.cast(obj);
66 if (other instanceof CodecDataObject) {
67 return data.equals(((CodecDataObject<?>) obj).data);
69 return codecAugmentedEquals(other);
73 public final String toString() {
74 return codecAugmentedFillToString(MoreObjects.toStringHelper(implementedInterface()).omitNullValues())
78 // TODO: consider switching to VarHandles for Java 9+
79 protected final Object codecMember(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
80 final NodeContextSupplier supplier) {
81 final Object cached = updater.get(this);
82 return cached != null ? unmaskNull(cached) : loadMember(updater, supplier);
85 protected final Object codecMember(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
86 final IdentifiableItemCodec codec) {
87 final Object cached = updater.get(this);
88 return cached != null ? unmaskNull(cached) : loadKey(updater, codec);
91 protected abstract int codecHashCode();
93 protected abstract boolean codecEquals(T other);
95 protected abstract ToStringHelper codecFillToString(ToStringHelper helper);
97 @SuppressWarnings("rawtypes")
98 final @NonNull NormalizedNodeContainer codecData() {
102 // Non-final to allow specialization in AugmentableCodecDataObject
103 int codecAugmentedHashCode() {
104 return codecHashCode();
107 // Non-final to allow specialization in AugmentableCodecDataObject
108 boolean codecAugmentedEquals(final T other) {
109 return codecEquals(other);
112 // Non-final to allow specialization in AugmentableCodecDataObject
113 ToStringHelper codecAugmentedFillToString(final ToStringHelper helper) {
114 return codecFillToString(helper);
117 // Helpers split out of codecMember to aid its inlining
118 private Object loadMember(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
119 final NodeContextSupplier supplier) {
120 final NodeCodecContext context = supplier.get();
122 @SuppressWarnings("unchecked")
123 final Optional<NormalizedNode<?, ?>> child = data.getChild(context.getDomPathArgument());
125 // We do not want to use Optional.map() here because we do not want to invoke defaultObject() when we have
126 // normal value because defaultObject() may end up throwing an exception intentionally.
127 return updateCache(updater, child.isPresent() ? context.deserializeObject(child.get())
128 : context.defaultObject());
131 // Helpers split out of codecMember to aid its inlining
132 private Object loadKey(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
133 final IdentifiableItemCodec codec) {
134 verify(data instanceof MapEntryNode, "Unsupported value %s", data);
135 return updateCache(updater, codec.deserialize(((MapEntryNode) data).getIdentifier()).getKey());
138 private Object updateCache(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
140 return updater.compareAndSet(this, null, maskNull(obj)) ? obj : unmaskNull(updater.get(this));
143 private static @NonNull Object maskNull(final @Nullable Object unmasked) {
144 return unmasked == null ? NULL_VALUE : unmasked;
147 private static @Nullable Object unmaskNull(final Object masked) {
148 return masked == NULL_VALUE ? null : masked;