*/
package org.opendaylight.mdsal.binding.dom.codec.impl;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.opendaylight.mdsal.binding.dom.codec.util.AugmentationReader;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
import org.opendaylight.yangtools.yang.binding.Augmentable;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
private static final String GET_IMPLEMENTED_INTERFACE = "getImplementedInterface";
private static final String TO_STRING = "toString";
private static final String EQUALS = "equals";
- private static final String GET_AUGMENTATION = "getAugmentation";
private static final String HASHCODE = "hashCode";
private static final String AUGMENTATIONS = "augmentations";
private static final Object NULL_VALUE = new Object();
@SuppressWarnings({ "rawtypes", "unchecked" })
LazyDataObject(final DataObjectCodecContext<D,?> ctx, final NormalizedNodeContainer data) {
- this.context = Preconditions.checkNotNull(ctx, "Context must not be null");
- this.data = Preconditions.checkNotNull(data, "Data must not be null");
+ this.context = requireNonNull(ctx, "Context must not be null");
+ this.data = requireNonNull(data, "Data must not be null");
}
@Override
return getAugmentationsImpl();
}
return getBindingData(method);
- } else if (GET_AUGMENTATION.equals(method.getName())) {
+ } else if (BindingMapping.AUGMENTABLE_AUGMENTATION_NAME.equals(method.getName())) {
return getAugmentationImpl((Class<?>) args[0]);
} else if (EQUALS.equals(method.getName())) {
return bindingEquals(args[0]);
Object cached = cachedData.get(method);
if (cached == null) {
final Object readedValue = context.getBindingChildValue(method, data);
- if (readedValue == null) {
- cached = NULL_VALUE;
- } else {
- cached = readedValue;
+ cached = readedValue == null ? NULL_VALUE : readedValue;
+
+ final Object raced = cachedData.putIfAbsent(method, cached);
+ if (raced != null) {
+ // Load/store raced, we should return the stored value
+ cached = raced;
}
- cachedData.putIfAbsent(method, cached);
}
return cached == NULL_VALUE ? null : cached;
@Override
public Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object obj) {
- Preconditions.checkArgument(this == Proxy.getInvocationHandler(obj),
+ checkArgument(this == Proxy.getInvocationHandler(obj),
"Supplied object is not associated with this proxy handler");
return getAugmentationsImpl();
}
private Object getAugmentationImpl(final Class<?> cls) {
+ requireNonNull(cls, "Supplied augmentation must not be null.");
+
final ImmutableMap<Class<? extends Augmentation<?>>, Augmentation<?>> aug = cachedAugmentations;
if (aug != null) {
return aug.get(cls);
}
- Preconditions.checkNotNull(cls,"Supplied augmentation must not be null.");
@SuppressWarnings({"unchecked","rawtypes"})
- final Optional<DataContainerCodecContext<?, ?>> augCtx = context.possibleStreamChild((Class) cls);
- if (augCtx.isPresent()) {
- final Optional<NormalizedNode<?, ?>> augData = data.getChild(augCtx.get().getDomPathArgument());
- if (augData.isPresent()) {
- return augCtx.get().deserialize(augData.get());
+ final Optional<DataContainerCodecContext<?, ?>> optAugCtx = context.possibleStreamChild((Class) cls);
+ if (optAugCtx.isPresent()) {
+ final DataContainerCodecContext<?, ?> augCtx = optAugCtx.get();
+ // Due to binding specification not representing grouping instantiations we can end up having the same
+ // augmentation applied to a grouping multiple times. While these augmentations have the same shape, they
+ // are still represented by distinct binding classes and therefore we need to make sure the result matches
+ // the augmentation the user is requesting -- otherwise a strict receiver would end up with a cryptic
+ // ClassCastException.
+ if (cls.isAssignableFrom(augCtx.getBindingClass())) {
+ final java.util.Optional<NormalizedNode<?, ?>> augData = data.getChild(augCtx.getDomPathArgument());
+ if (augData.isPresent()) {
+ return augCtx.deserialize(augData.get());
+ }
}
}
return null;
public String bindingToString() {
final ToStringHelper helper = MoreObjects.toStringHelper(context.getBindingClass()).omitNullValues();
- for (final Method m :context.getHashCodeAndEqualsMethods()) {
+ for (final Method m : context.getHashCodeAndEqualsMethods()) {
helper.add(m.getName(), getBindingData(m));
}
if (Augmentable.class.isAssignableFrom(context.getBindingClass())) {