import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.binding.dom.codec.util.AugmentationReader;
import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
*/
public abstract class AugmentableCodecDataObject<T extends DataObject & Augmentable<T>>
extends CodecDataObject<T> implements Augmentable<T>, AugmentationHolder<T> {
+ private final @NonNull DataObjectCodecContext<T, ?> context;
+
@SuppressWarnings("rawtypes")
private static final AtomicReferenceFieldUpdater<AugmentableCodecDataObject, ImmutableClassToInstanceMap>
CACHED_AUGMENTATIONS_UPDATER = AtomicReferenceFieldUpdater.newUpdater(AugmentableCodecDataObject.class,
ImmutableClassToInstanceMap.class, "cachedAugmentations");
private volatile ImmutableClassToInstanceMap<Augmentation<T>> cachedAugmentations;
- public AugmentableCodecDataObject(final DataObjectCodecContext<T, ?> ctx,
+ public AugmentableCodecDataObject(final DataObjectCodecContext<T, ?> context,
final NormalizedNodeContainer<?, ?, ?> data) {
- super(ctx, data);
+ super(data);
+ this.context = requireNonNull(context, "Context must not be null");
}
@Override
*/
package org.opendaylight.mdsal.binding.dom.codec.impl;
+import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
/**
@SuppressWarnings("rawtypes")
private final @NonNull NormalizedNodeContainer data;
- final @NonNull DataObjectCodecContext<T, ?> context;
private volatile Integer cachedHashcode = null;
- public CodecDataObject(final DataObjectCodecContext<T, ?> ctx, final NormalizedNodeContainer<?, ?, ?> data) {
- this.context = requireNonNull(ctx, "Context must not be null");
+ public CodecDataObject(final NormalizedNodeContainer<?, ?, ?> data) {
this.data = requireNonNull(data, "Data must not be null");
}
// TODO: consider switching to VarHandles for Java 9+
protected final Object codecMember(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
- final int offset) {
+ final NodeContextSupplier supplier) {
final Object cached = updater.get(this);
- return cached != null ? unmaskNull(cached) : loadMember(updater, offset);
+ return cached != null ? unmaskNull(cached) : loadMember(updater, supplier);
+ }
+
+ protected final Object codecMember(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
+ final IdentifiableItemCodec codec) {
+ final Object cached = updater.get(this);
+ return cached != null ? unmaskNull(cached) : loadKey(updater, codec);
}
protected abstract int codecHashCode();
// Helpers split out of codecMember to aid its inlining
private Object loadMember(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
- final int offset) {
- final Object decoded = context.getBindingChildValue(data, offset);
- return updater.compareAndSet(this, null, maskNull(decoded)) ? decoded : unmaskNull(updater.get(this));
+ final NodeContextSupplier supplier) {
+ final NodeCodecContext context = supplier.get();
+
+ @SuppressWarnings("unchecked")
+ final Optional<NormalizedNode<?, ?>> child = data.getChild(context.getDomPathArgument());
+
+ // We do not want to use Optional.map() here because we do not want to invoke defaultObject() when we have
+ // normal value because defaultObject() may end up throwing an exception intentionally.
+ return updateCache(updater, child.isPresent() ? context.deserializeObject(child.get())
+ : context.defaultObject());
+ }
+
+ // Helpers split out of codecMember to aid its inlining
+ private Object loadKey(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
+ final IdentifiableItemCodec codec) {
+ verify(data instanceof MapEntryNode, "Unsupported value %s", data);
+ return updateCache(updater, codec.deserialize(((MapEntryNode) data).getIdentifier()).getKey());
+ }
+
+ private Object updateCache(final AtomicReferenceFieldUpdater<CodecDataObject<?>, Object> updater,
+ final Object obj) {
+ return updater.compareAndSet(this, null, maskNull(obj)) ? obj : unmaskNull(updater.get(this));
}
private static @NonNull Object maskNull(final @Nullable Object unmasked) {
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.codec.impl;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Bridge for initializing {@link CodecDataObject} instance constants during class loading time. This class is public
+ * only due to implementation restrictions and can change at any time.
+ */
+@Beta
+public final class CodecDataObjectBridge {
+ private static final ThreadLocal<CodecDataObjectCustomizer> CURRENT_CUSTOMIZER = new ThreadLocal<>();
+
+ private CodecDataObjectBridge() {
+
+ }
+
+ public static @NonNull NodeContextSupplier resolve(final @NonNull String methodName) {
+ return current().resolve(methodName);
+ }
+
+ public static @NonNull IdentifiableItemCodec resolveKey(final @NonNull String methodName) {
+ return current().resolveKey(methodName);
+ }
+
+ static @Nullable CodecDataObjectCustomizer setup(final @NonNull CodecDataObjectCustomizer next) {
+ final CodecDataObjectCustomizer prev = CURRENT_CUSTOMIZER.get();
+ CURRENT_CUSTOMIZER.set(verifyNotNull(next));
+ return prev;
+ }
+
+ static void tearDown(final @Nullable CodecDataObjectCustomizer prev) {
+ if (prev == null) {
+ CURRENT_CUSTOMIZER.remove();
+ } else {
+ CURRENT_CUSTOMIZER.set(prev);
+ }
+ }
+
+ private static @NonNull CodecDataObjectCustomizer current() {
+ return verifyNotNull(CURRENT_CUSTOMIZER.get(), "No customizer attached");
+ }
+}
*/
package org.opendaylight.mdsal.binding.dom.codec.impl;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
+import java.util.Map.Entry;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException;
+import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.Customizer;
* Private support for generating AbstractDataObject specializations.
*/
final class CodecDataObjectCustomizer implements Customizer {
- static final int KEY_OFFSET = -1;
-
private static final Logger LOG = LoggerFactory.getLogger(CodecDataObjectCustomizer.class);
private static final CtClass CT_ARFU = StaticClassPool.findClass(AtomicReferenceFieldUpdater.class);
private static final CtClass CT_BOOLEAN = StaticClassPool.findClass(boolean.class);
+ private static final CtClass CT_DATAOBJECT = StaticClassPool.findClass(DataObject.class);
+ private static final CtClass CT_HELPER = StaticClassPool.findClass(ToStringHelper.class);
+ private static final CtClass CT_IIC = StaticClassPool.findClass(IdentifiableItemCodec.class);
private static final CtClass CT_INT = StaticClassPool.findClass(int.class);
+ private static final CtClass CT_NCS = StaticClassPool.findClass(NodeContextSupplier.class);
private static final CtClass CT_OBJECT = StaticClassPool.findClass(Object.class);
- private static final CtClass CT_HELPER = StaticClassPool.findClass(ToStringHelper.class);
- private static final CtClass CT_DATAOBJECT = StaticClassPool.findClass(DataObject.class);
private static final CtClass[] EMPTY_ARGS = new CtClass[0];
private static final CtClass[] EQUALS_ARGS = new CtClass[] { CT_DATAOBJECT };
private static final CtClass[] TOSTRING_ARGS = new CtClass[] { CT_HELPER };
- private final List<Method> properties;
- private final Method keyMethod;
+ private final ImmutableMap<Method, NodeContextSupplier> properties;
+ private final Entry<Method, IdentifiableItemCodec> keyMethod;
- CodecDataObjectCustomizer(final List<Method> properties, final @Nullable Method keyMethod) {
+ CodecDataObjectCustomizer(final ImmutableMap<Method, NodeContextSupplier> properties,
+ final @Nullable Entry<Method, IdentifiableItemCodec> keyMethod) {
this.properties = requireNonNull(properties);
this.keyMethod = keyMethod;
}
LOG.trace("Generating class {}", generated.getName());
generated.addInterface(bindingClass);
- int offset = 0;
- for (Method method : properties) {
- generateMethod(loader, generated, method, offset++);
+ for (Method method : properties.keySet()) {
+ generateMethod(loader, generated, method, CT_NCS, "resolve");
}
if (keyMethod != null) {
- generateMethod(loader, generated, keyMethod, KEY_OFFSET);
+ generateMethod(loader, generated, keyMethod.getKey(), CT_IIC, "resolveKey");
}
// Final bits: codecHashCode() ...
return ImmutableList.of();
}
+ @Override
+ public Class<?> customizeLoading(final @NonNull Supplier<Class<?>> loader) {
+ final CodecDataObjectCustomizer prev = CodecDataObjectBridge.setup(this);
+ try {
+ final Class<?> result = loader.get();
+
+ /*
+ * This a bit of magic to support NodeContextSupplier constants. These constants need to be resolved while
+ * we have the information needed to find them -- that information is being held in this instance and we
+ * leak it to a thread-local variable held by CodecDataObjectBridge.
+ *
+ * By default the JVM will defer class initialization to first use, which unfortunately is too late for
+ * us, and hence we need to force class to initialize.
+ */
+ try {
+ Class.forName(result.getName(), true, result.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new LinkageError("Failed to find newly-defined " + result, e);
+ }
+
+ return result;
+ } finally {
+ CodecDataObjectBridge.tearDown(prev);
+ }
+ }
+
+
+ @NonNull NodeContextSupplier resolve(final @NonNull String methodName) {
+ final Optional<Entry<Method, NodeContextSupplier>> found = properties.entrySet().stream()
+ .filter(entry -> methodName.equals(entry.getKey().getName())).findAny();
+ verify(found.isPresent(), "Failed to find property for %s in %s", methodName, this);
+ return verifyNotNull(found.get().getValue());
+ }
+
+ @NonNull IdentifiableItemCodec resolveKey(final @NonNull String methodName) {
+ return verifyNotNull(verifyNotNull(keyMethod, "No key method attached for %s in %s", methodName, this)
+ .getValue());
+ }
+
private String codecHashCodeBody() {
final StringBuilder sb = new StringBuilder()
.append("{\n")
.append("final int prime = 31;\n")
.append("int result = 1;\n");
- for (Method method : properties) {
+ for (Method method : properties.keySet()) {
sb.append("result = prime * result + java.util.").append(utilClass(method)).append(".hashCode(")
.append(method.getName()).append("());\n");
}
.append("final ").append(ifaceName).append(" other = $1;")
.append("return true");
- for (Method method : properties) {
+ for (Method method : properties.keySet()) {
final String methodName = method.getName();
sb.append("\n&& java.util.").append(utilClass(method)).append(".equals(").append(methodName)
.append("(), other.").append(methodName).append("())");
final StringBuilder sb = new StringBuilder()
.append("{\n")
.append("return $1");
- for (Method method : properties) {
+ for (Method method : properties.keySet()) {
final String methodName = method.getName();
sb.append("\n.add(\"").append(methodName).append("\", ").append(methodName).append("())");
}
}
private static void generateMethod(final CodecClassLoader loader, final CtClass generated, final Method method,
- final int offset) throws CannotCompileException, NotFoundException {
+ final CtClass contextType, final String resolveMethod) throws CannotCompileException, NotFoundException {
LOG.trace("Generating for method {}", method);
final String methodName = method.getName();
final String methodArfu = methodName + "$$$ARFU";
+ final String methodNcs = methodName + "$$$NCS";
+
+ // NodeContextSupplier ...
+ final CtField ncsField = new CtField(contextType, methodNcs, generated);
+ ncsField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
+ generated.addField(ncsField, new StringBuilder().append(CodecDataObjectBridge.class.getName())
+ .append('.').append(resolveMethod).append("(\"").append(methodName).append("\")").toString());
- // AtomicReferenceFieldUpdater ...
+ // ... AtomicReferenceFieldUpdater ...
final CtField arfuField = new CtField(CT_ARFU, methodArfu, generated);
arfuField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
generated.addField(arfuField, new StringBuilder().append(CT_ARFU.getName()).append(".newUpdater(")
getter.setBody(new StringBuilder()
.append("{\n")
- .append("return (").append(retName).append(") codecMember(").append(methodArfu).append(", ").append(offset)
- .append(");\n")
+ .append("return (").append(retName).append(") codecMember(").append(methodArfu).append(", ")
+ .append(methodNcs).append(");\n")
.append('}').toString());
getter.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
generated.addMethod(getter);
*/
package org.opendaylight.mdsal.binding.dom.codec.impl;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
abstract class DataContainerCodecContext<D extends DataObject, T extends WithStatus> extends NodeCodecContext
implements BindingDataObjectCodecTreeNode<D> {
+ private final @NonNull DataContainerCodecPrototype<T> prototype;
- private final DataContainerCodecPrototype<T> prototype;
private volatile DataObjectSerializer eventStreamSerializer;
protected DataContainerCodecContext(final DataContainerCodecPrototype<T> prototype) {
- this.prototype = prototype;
+ this.prototype = requireNonNull(prototype);
}
@Override
if (schema instanceof ContainerSchemaNode) {
return new ContainerNodeCodecContext(this);
} else if (schema instanceof ListSchemaNode) {
- return Identifiable.class.isAssignableFrom(getBindingClass()) ? new KeyedListNodeCodecContext(this)
- : new ListNodeCodecContext(this);
+ return Identifiable.class.isAssignableFrom(getBindingClass())
+ ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListSchemaNode>) this)
+ : new ListNodeCodecContext(this);
} else if (schema instanceof ChoiceSchemaNode) {
return new ChoiceNodeCodecContext(this);
} else if (schema instanceof AugmentationSchemaNode) {
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
}
private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class);
- private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, DataObjectCodecContext.class,
- NormalizedNodeContainer.class);
+ private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, NormalizedNodeContainer.class);
+ private static final MethodType AUGMENTABLE_CONSTRUCTOR_TYPE = MethodType.methodType(void.class,
+ DataObjectCodecContext.class, NormalizedNodeContainer.class);
private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class,
+ NormalizedNodeContainer.class);
+ private static final MethodType AUGMENTABLE_DATAOBJECT_TYPE = MethodType.methodType(DataObject.class,
DataObjectCodecContext.class, NormalizedNodeContainer.class);
private static final Comparator<Method> METHOD_BY_ALPHABET = Comparator.comparing(Method::getName);
private static final Augmentations EMPTY_AUGMENTATIONS = new Augmentations(ImmutableMap.of(), ImmutableMap.of());
private static final CtClass SUPERCLASS = StaticClassPool.findClass(CodecDataObject.class);
private static final CtClass AUGMENTABLE_SUPERCLASS = StaticClassPool.findClass(
AugmentableCodecDataObject.class);
- private static final NodeContextSupplier[] EMPTY_METHODS = new NodeContextSupplier[0];
private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
private final ImmutableMap<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYang;
private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
private final ImmutableMap<AugmentationIdentifier, Type> possibleAugmentations;
- private final NodeContextSupplier[] byMethod;
private final MethodHandle proxyConstructor;
@SuppressWarnings("rawtypes")
this(prototype, null);
}
- DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype, final @Nullable Method keyMethod) {
+ DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
+ final Entry<Method, IdentifiableItemCodec> keyMethod) {
super(prototype);
final Class<D> bindingClass = getBindingClass();
}
// Make sure properties are alpha-sorted
- final List<Method> properties = new ArrayList<>(tmpMethodToSupplier.keySet());
- properties.sort(METHOD_BY_ALPHABET);
- if (!properties.isEmpty()) {
- byMethod = new NodeContextSupplier[properties.size()];
- int offset = 0;
- for (Method prop : properties) {
- byMethod[offset++] = verifyNotNull(tmpMethodToSupplier.get(prop));
- }
- } else {
- byMethod = EMPTY_METHODS;
+ final Method[] properties = tmpMethodToSupplier.keySet().toArray(new Method[0]);
+ Arrays.sort(properties, METHOD_BY_ALPHABET);
+ final Builder<Method, NodeContextSupplier> propBuilder = ImmutableMap.builderWithExpectedSize(
+ properties.length);
+ for (Method prop : properties) {
+ propBuilder.put(prop, verifyNotNull(tmpMethodToSupplier.get(prop)));
}
this.byYang = ImmutableMap.copyOf(byYangBuilder);
this.byBindingArgClass = ImmutableMap.copyOf(byBindingArgClassBuilder);
final CtClass superClass;
+ final MethodType ctorType;
if (Augmentable.class.isAssignableFrom(bindingClass)) {
this.possibleAugmentations = factory().getRuntimeContext().getAvailableAugmentationTypes(getSchema());
superClass = AUGMENTABLE_SUPERCLASS;
+ ctorType = AUGMENTABLE_CONSTRUCTOR_TYPE;
} else {
this.possibleAugmentations = ImmutableMap.of();
superClass = SUPERCLASS;
+ ctorType = CONSTRUCTOR_TYPE;
}
reloadAllAugmentations();
final Class<?> generatedClass;
try {
generatedClass = prototype.getFactory().getLoader().generateSubclass(superClass, bindingClass, "codecImpl",
- new CodecDataObjectCustomizer(properties, keyMethod));
+ new CodecDataObjectCustomizer(propBuilder.build(), keyMethod));
} catch (CannotCompileException | IOException | NotFoundException e) {
throw new LinkageError("Failed to generated class for " + bindingClass, e);
}
+ final MethodHandle ctor;
try {
- proxyConstructor = MethodHandles.publicLookup().findConstructor(generatedClass, CONSTRUCTOR_TYPE)
- .asType(DATAOBJECT_TYPE).bindTo(this);
+ ctor = MethodHandles.publicLookup().findConstructor(generatedClass, ctorType);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new LinkageError("Failed to find contructor for class " + generatedClass, e);
}
+
+ if (Augmentable.class.isAssignableFrom(bindingClass)) {
+ proxyConstructor = ctor.asType(AUGMENTABLE_DATAOBJECT_TYPE).bindTo(this);
+ } else {
+ proxyConstructor = ctor.asType(DATAOBJECT_TYPE);
+ }
}
// This method could be synchronized, but that would mean that concurrent attempts to load an invalid augmentation
return DataContainerCodecPrototype.from(augClass, augSchema.getKey(), augSchema.getValue(), factory());
}
- @SuppressWarnings("rawtypes")
- @Nullable Object getBindingChildValue(final NormalizedNodeContainer domData, final int offset) {
- final NodeCodecContext childContext = byMethod[offset].get();
-
- @SuppressWarnings("unchecked")
- final Optional<NormalizedNode<?, ?>> domChild = domData.getChild(childContext.getDomPathArgument());
-
- // We do not want to use Optional.map() here because we do not want to invoke defaultObject() when we have
- // normal value because defaultObject() may end up throwing an exception intentionally.
- return domChild.isPresent() ? childContext.deserializeObject(domChild.get()) : childContext.defaultObject();
- }
-
@SuppressWarnings("checkstyle:illegalCatch")
protected final D createBindingProxy(final NormalizedNodeContainer<?, ?, ?> node) {
try {
import static java.util.Objects.requireNonNull;
+import com.google.common.annotations.Beta;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import java.lang.invoke.MethodHandle;
import org.opendaylight.yangtools.util.ImmutableOffsetMap;
import org.opendaylight.yangtools.util.ImmutableOffsetMapTemplate;
import org.opendaylight.yangtools.util.SharedSingletonMapTemplate;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
import org.opendaylight.yangtools.yang.binding.Identifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-abstract class IdentifiableItemCodec implements Codec<NodeIdentifierWithPredicates, IdentifiableItem<?, ?>> {
+/**
+ * Codec support for extracting the {@link Identifiable#key()} method return from a MapEntryNode. This class is public
+ * only because implementation restrictions and can change at any time.
+ */
+@Beta
+public abstract class IdentifiableItemCodec implements Codec<NodeIdentifierWithPredicates, IdentifiableItem<?, ?>> {
private static final class SingleKey extends IdentifiableItemCodec {
private static final MethodType CTOR_TYPE = MethodType.methodType(Identifier.class, Object.class);
*/
package org.opendaylight.mdsal.binding.dom.codec.impl;
+import static java.util.Objects.requireNonNull;
import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.IDENTIFIABLE_KEY_NAME;
import java.lang.reflect.Method;
+import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.List;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Identifiable;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
final class KeyedListNodeCodecContext<D extends DataObject & Identifiable<?>> extends ListNodeCodecContext<D> {
private final IdentifiableItemCodec codec;
- KeyedListNodeCodecContext(final DataContainerCodecPrototype<ListSchemaNode> prototype) {
- super(prototype, keyMethod(prototype.getBindingClass()));
- this.codec = factory().getPathArgumentCodec(getBindingClass(), getSchema());
+ private KeyedListNodeCodecContext(final DataContainerCodecPrototype<ListSchemaNode> prototype,
+ final Method keyMethod, final IdentifiableItemCodec codec) {
+ super(prototype, new SimpleImmutableEntry<>(keyMethod, codec));
+ this.codec = requireNonNull(codec);
}
- private static Method keyMethod(final Class<?> bindingClass) {
+ @SuppressWarnings("rawtypes")
+ static KeyedListNodeCodecContext create(final DataContainerCodecPrototype<ListSchemaNode> prototype) {
+ final Class<?> bindingClass = prototype.getBindingClass();
+ final Method keyMethod;
try {
- // This just verifies the method is present
- return bindingClass.getMethod(IDENTIFIABLE_KEY_NAME);
+ keyMethod = bindingClass.getMethod(IDENTIFIABLE_KEY_NAME);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Required method not available", e);
}
+
+ final IdentifiableItemCodec codec = prototype.getFactory().getPathArgumentCodec(bindingClass,
+ prototype.getSchema());
+ return new KeyedListNodeCodecContext<>(prototype, keyMethod, codec);
}
@Override
}
}
- @Override
- @SuppressWarnings("rawtypes")
- Object getBindingChildValue(final NormalizedNodeContainer dom, final int offset) {
- if (offset == CodecDataObjectCustomizer.KEY_OFFSET && dom instanceof MapEntryNode) {
- NodeIdentifierWithPredicates identifier = ((MapEntryNode) dom).getIdentifier();
- return codec.deserialize(identifier).getKey();
- }
- return super.getBindingChildValue(dom, offset);
- }
-
@Override
protected InstanceIdentifier.PathArgument getBindingPathArgument(final YangInstanceIdentifier.PathArgument domArg) {
if (domArg instanceof NodeIdentifierWithPredicates) {
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map.Entry;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-class ListNodeCodecContext<D extends DataObject> extends DataObjectCodecContext<D,ListSchemaNode> {
+class ListNodeCodecContext<D extends DataObject> extends DataObjectCodecContext<D, ListSchemaNode> {
ListNodeCodecContext(final DataContainerCodecPrototype<ListSchemaNode> prototype) {
super(prototype);
}
- ListNodeCodecContext(final DataContainerCodecPrototype<ListSchemaNode> prototype, final Method keyMethod) {
+ ListNodeCodecContext(final DataContainerCodecPrototype<ListSchemaNode> prototype,
+ final Entry<Method, IdentifiableItemCodec> keyMethod) {
super(prototype, keyMethod);
}
*/
package org.opendaylight.mdsal.binding.dom.codec.impl;
-import java.util.function.Supplier;
-import org.eclipse.jdt.annotation.NonNull;
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
/**
- * Type capture of an entity producing NodeCodecContexts.
+ * Type capture of an entity producing NodeCodecContexts. Implementations are required to perform memoization. This
+ * interface does not form API surface and is exposed only for generated code. It can change at any time.
*/
-interface NodeContextSupplier extends Supplier<NodeCodecContext> {
- @Override
- @NonNull NodeCodecContext get();
+@Beta
+@FunctionalInterface
+@NonNullByDefault
+public interface NodeContextSupplier {
+ NodeCodecContext get();
}