+ @Override
+ public YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier<?> binding) {
+ return instanceIdentifierCodec.fromBinding(binding);
+ }
+
+ @Override
+ public <T extends DataObject> InstanceIdentifier<T> fromYangInstanceIdentifier(final YangInstanceIdentifier dom) {
+ return instanceIdentifierCodec.toBinding(dom);
+ }
+
+ @Override
+ public <A extends Augmentation<?>> AugmentationResult toNormalizedAugmentation(final InstanceIdentifier<A> path,
+ final A data) {
+ final var result = toNormalizedNode(path, data);
+ if (result instanceof AugmentationResult augment) {
+ return augment;
+ }
+ throw new IllegalArgumentException(path + " does not identify an Augmentation");
+ }
+
+ @Override
+ public <T extends DataObject> NodeResult toNormalizedDataObject(final InstanceIdentifier<T> path, final T data) {
+ final var result = toNormalizedNode(path, data);
+ if (result instanceof NodeResult node) {
+ return node;
+ }
+ throw new IllegalArgumentException(path + " does not identify a plain DataObject");
+ }
+
+ @Override
+ public <T extends DataObject> NormalizedResult toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
+ // We create Binding Stream Writer which translates from Binding to Normalized Nodes
+ final var yangArgs = new ArrayList<PathArgument>();
+ final var codecContext = getCodecContextNode(path, yangArgs);
+ final var yangPath = YangInstanceIdentifier.of(yangArgs);
+
+ // We create DOM stream writer which produces normalized nodes
+ final var result = new NormalizationResultHolder();
+ final var domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+ final var bindingWriter = new BindingToNormalizedStreamWriter(codecContext, domWriter);
+ final var augment = codecContext instanceof BindingAugmentationCodecTreeNode<?> augmentNode ? augmentNode
+ : null;
+
+ try {
+ // Augmentations do not have a representation, so we are faking a ContainerNode as the parent and we will be
+ // extracting the resulting children.
+ if (augment != null) {
+ domWriter.startContainerNode(FAKE_NODEID, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+ }
+
+ // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result
+ getSerializer(path.getTargetType()).serialize(data, bindingWriter);
+
+ if (augment != null) {
+ domWriter.endNode();
+ }
+ } catch (final IOException e) {
+ LOG.error("Unexpected failure while serializing path {} data {}", path, data, e);
+ throw new IllegalStateException("Failed to create normalized node", e);
+ }
+
+ // Terminate the fake container and extract it to the result
+ if (augment != null) {
+ return new AugmentationResult(yangPath, augment.childPathArguments(),
+ ImmutableList.copyOf(((ContainerNode) result.getResult().data()).body()));
+ }
+ return new NodeResult(yangPath, result.getResult().data());
+ }
+
+ @Override
+ public Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path,
+ final NormalizedNode data) {
+ if (notBindingRepresentable(data)) {
+ return null;
+ }
+
+ final var builder = new ArrayList<InstanceIdentifier.PathArgument>();
+ final var codec = getCodecContextNode(path, builder);
+ if (codec == null) {
+ if (data != null) {
+ LOG.warn("Path {} does not have a binding equivalent, should have been caught earlier ({})", path,
+ data.getClass());
+ }
+ return null;
+ }
+
+ final DataObject lazyObj = codec.deserialize(data);
+ final InstanceIdentifier<?> bindingPath = InstanceIdentifier.unsafeOf(builder);
+ return Map.entry(bindingPath, lazyObj);
+ }
+
+ @Override
+ public BaseNotification fromNormalizedNodeNotification(final Absolute path, final ContainerNode data) {
+ return getNotificationContext(path).deserialize(data);
+ }
+
+ @Override
+ public BaseNotification fromNormalizedNodeNotification(final Absolute path, final ContainerNode data,
+ final Instant eventInstant) {
+ return eventInstant == null ? fromNormalizedNodeNotification(path, data)
+ : getNotificationContext(path).deserialize(data, eventInstant);
+ }
+
+ @Override
+ public DataObject fromNormalizedNodeRpcData(final Absolute containerPath, final ContainerNode data) {
+ return getRpcInputCodec(containerPath).deserialize(data);
+ }
+
+ @Override
+ public <T extends RpcInput> T fromNormalizedNodeActionInput(final Class<? extends Action<?, ?, ?>> action,
+ final ContainerNode input) {
+ return (T) requireNonNull(getActionCodec(action).input().deserialize(requireNonNull(input)));
+ }
+
+ @Override
+ public <T extends RpcOutput> T fromNormalizedNodeActionOutput(final Class<? extends Action<?, ?, ?>> action,
+ final ContainerNode output) {
+ return (T) requireNonNull(getActionCodec(action).output().deserialize(requireNonNull(output)));
+ }
+
+ @Override
+ @SuppressFBWarnings("BC_UNCONFIRMED_CAST")
+ public ContainerNode toNormalizedNodeNotification(final Notification<?> data) {
+ // FIXME: Should the cast to DataObject be necessary?
+ return serializeDataObject((DataObject) data,
+ (ctx, iface, domWriter) -> ctx.newNotificationWriter(
+ (Class<? extends Notification<?>>) iface.asSubclass(Notification.class), domWriter));
+ }
+
+ @Override
+ public ContainerNode toNormalizedNodeNotification(final Absolute path, final BaseNotification data) {
+ checkArgument(data instanceof DataObject, "Unexpected data %s", data);
+ @SuppressWarnings("rawtypes")
+ final NotificationCodecContext notifContext = getNotificationContext(path);
+ @SuppressWarnings("unchecked")
+ final var result = notifContext.serialize((DataObject) data);
+ verify(result instanceof ContainerNode, "Unexpected result %s from %s", result, data);
+ return (ContainerNode) result;
+ }
+
+ @Override
+ @SuppressFBWarnings("BC_UNCONFIRMED_CAST")
+ public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
+ // FIXME: Should the cast to DataObject be necessary?
+ return serializeDataObject((DataObject) data, BindingNormalizedNodeWriterFactory::newRpcWriter);
+ }
+
+ @Override
+ public ContainerNode toNormalizedNodeActionInput(final Class<? extends Action<?, ?, ?>> action,
+ final RpcInput input) {
+ return serializeDataObject(input,(ctx, iface, domWriter) -> ctx.newActionInputWriter(action, domWriter));
+ }
+
+ @Override
+ public ContainerNode toNormalizedNodeActionOutput(final Class<? extends Action<?, ?, ?>> action,
+ final RpcOutput output) {
+ return serializeDataObject(output, (ctx, iface, domWriter) -> ctx.newActionOutputWriter(action, domWriter));
+ }
+
+ @Override
+ protected NodeIdentifier actionInputName(final Class<? extends Action<?, ?, ?>> action) {
+ return verifyNotNull(getActionCodec(action).input().getDomPathArgument());
+ }
+
+ @Override
+ protected NodeIdentifier actionOutputName(final Class<? extends Action<?, ?, ?>> action) {
+ return verifyNotNull(getActionCodec(action).output().getDomPathArgument());
+ }
+
+ private <T extends DataContainer> @NonNull ContainerNode serializeDataObject(final DataObject data,
+ final WriterFactoryMethod<T> newWriter) {
+ final var result = new NormalizationResultHolder();
+ // We create DOM stream writer which produces normalized nodes
+ final var domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+ final Class<? extends DataObject> type = data.implementedInterface();
+ @SuppressWarnings("unchecked")
+ final BindingStreamEventWriter writer = newWriter.createWriter(this, (Class<T>) type, domWriter);
+ try {
+ getSerializer(type).serialize(data, writer);
+ } catch (final IOException e) {
+ LOG.error("Unexpected failure while serializing data {}", data, e);
+ throw new IllegalStateException("Failed to create normalized node", e);
+ }
+ return (ContainerNode) result.getResult().data();
+ }
+
+ private static boolean notBindingRepresentable(final NormalizedNode data) {
+ // ValueNode covers LeafNode and LeafSetEntryNode
+ return data instanceof ValueNode
+ || data instanceof MapNode || data instanceof UnkeyedListNode
+ || data instanceof ChoiceNode
+ || data instanceof LeafSetNode;
+ }
+
+ private static <K,V> V getOrRethrow(final LoadingCache<K, V> cache, final K key) {
+ try {
+ return cache.getUnchecked(key);
+ } catch (UncheckedExecutionException e) {
+ final var cause = e.getCause();
+ if (cause != null) {
+ Throwables.throwIfUnchecked(cause);
+ }
+ throw e;
+ }
+ }
+