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 java.util.Objects.requireNonNull;
12 import com.google.common.base.MoreObjects;
13 import com.google.common.base.MoreObjects.ToStringHelper;
14 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.yangtools.yang.binding.DataObject;
18 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
21 * A base class for {@link DataObject}s backed by {@link DataObjectCodecContext}. While this class is public, it not
22 * part of API surface and is an implementation detail. The only reason for it being public is that it needs to be
23 * accessible by code generated at runtime.
25 * @param <T> DataObject type
27 public abstract class CodecDataObject<T extends DataObject> implements DataObject {
28 private static final @NonNull Object NULL_VALUE = new Object();
30 @SuppressWarnings("rawtypes")
31 private final @NonNull NormalizedNodeContainer data;
32 final @NonNull DataObjectCodecContext<T, ?> context;
34 private volatile Integer cachedHashcode = null;
36 public CodecDataObject(final DataObjectCodecContext<T, ?> ctx, final NormalizedNodeContainer<?, ?, ?> data) {
37 this.context = requireNonNull(ctx, "Context must not be null");
38 this.data = requireNonNull(data, "Data must not be null");
42 public final int hashCode() {
43 final Integer cached = cachedHashcode;
48 final int result = codecAugmentedHashCode();
49 cachedHashcode = result;
54 public final boolean equals(final Object obj) {
58 final Class<? extends DataObject> iface = implementedInterface();
59 if (!iface.isInstance(obj)) {
62 @SuppressWarnings("unchecked")
63 final T other = (T) iface.cast(obj);
64 if (other instanceof CodecDataObject) {
65 return data.equals(((CodecDataObject<?>) obj).data);
67 return codecAugmentedEquals(other);
71 public final String toString() {
72 return codecAugmentedFillToString(MoreObjects.toStringHelper(implementedInterface()).omitNullValues())
76 // TODO: consider switching to VarHandles for Java 9+
77 protected final Object codecMember(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
78 final String methodName) {
79 final Object cached = updater.get(this);
80 return cached != null ? unmaskNull(cached) : loadMember(updater, methodName);
83 protected abstract int codecHashCode();
85 protected abstract boolean codecEquals(T other);
87 protected abstract ToStringHelper codecFillToString(ToStringHelper helper);
89 @SuppressWarnings("rawtypes")
90 final @NonNull NormalizedNodeContainer codecData() {
94 // Non-final to allow specialization in AugmentableCodecDataObject
95 int codecAugmentedHashCode() {
96 return codecHashCode();
99 // Non-final to allow specialization in AugmentableCodecDataObject
100 boolean codecAugmentedEquals(final T other) {
101 return codecEquals(other);
104 // Non-final to allow specialization in AugmentableCodecDataObject
105 ToStringHelper codecAugmentedFillToString(final ToStringHelper helper) {
106 return codecFillToString(helper);
109 // Helpers split out of codecMember to aid its inlining
110 private Object loadMember(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
111 final String methodName) {
112 final Object decoded = context.getBindingChildValue(methodName, data);
113 return updater.compareAndSet(this, null, maskNull(decoded)) ? decoded : unmaskNull(updater.get(this));
116 private static @NonNull Object maskNull(final @Nullable Object unmasked) {
117 return unmasked == null ? NULL_VALUE : unmasked;
120 private static @Nullable Object unmaskNull(final Object masked) {
121 return masked == NULL_VALUE ? null : masked;