/* * Copyright (c) 2023 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 com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.lang.invoke.MethodHandle; import java.util.List; import java.util.Map; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException; import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus; /** * Abstract base for {@link DataObjectCodecContext} and {@link AugmentationCodecContext}. They share most of their * mechanics, but notably: *
    *
  1. DataObjectCodecContext has an exact DistinctNodeContainer and YangInstanceIdentifier mapping and can be the * target of augmentations (i.e. can implement Augmentable contract)
  2. *
  3. AugmentationNodeContext has neither of those traits and really is just a filter of its parent * DistinctNodeContainer
  4. *
* *

* Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy. * *

* While this class is public, it not part of API surface and is an implementation detail. The only reason for it being * public is that it needs to be accessible by code generated at runtime. */ public abstract sealed class AbstractDataObjectCodecContext extends DataContainerCodecContext permits AugmentationCodecContext, DataObjectCodecContext { private final ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; private final ImmutableMap, DataContainerCodecPrototype> byStreamClass; private final ImmutableMap byYang; private final ImmutableMap leafChild; private final MethodHandle proxyConstructor; AbstractDataObjectCodecContext(final DataContainerCodecPrototype prototype, final CodecDataObjectAnalysis analysis) { super(prototype); byBindingArgClass = analysis.byBindingArgClass; byStreamClass = analysis.byStreamClass; byYang = analysis.byYang; leafChild = analysis.leafNodes; proxyConstructor = analysis.proxyConstructor; } @Override public final WithStatus getSchema() { // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps? return (WithStatus) getType().statement(); } @Override public final DataContainerCodecContext getStreamChild(final Class childClass) { return childNonNull(streamChild(childClass), childClass, "Child %s is not valid child of %s", getBindingClass(), childClass); } @SuppressWarnings("unchecked") @Override public final DataContainerCodecContext streamChild(final Class childClass) { final var childProto = streamChildPrototype(childClass); return childProto == null ? null : (DataContainerCodecContext) childProto.get(); } @Nullable DataContainerCodecPrototype streamChildPrototype(final @NonNull Class childClass) { return byStreamClass.get(childClass); } @Override public final DataContainerCodecContext bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg, final List builder) { final var argType = arg.getType(); final var context = childNonNull(pathChildPrototype(argType), argType, "Class %s is not valid child of %s", argType, getBindingClass()) .get(); if (context instanceof ChoiceCodecContext choice) { choice.addYangPathArgument(arg, builder); final var caseType = arg.getCaseType(); final var type = arg.getType(); final DataContainerCodecContext caze; if (caseType.isPresent()) { // Non-ambiguous addressing this should not pose any problems caze = choice.getStreamChild(caseType.orElseThrow()); } else { caze = choice.getCaseByChildClass(type); } caze.addYangPathArgument(arg, builder); return caze.bindingPathArgumentChild(arg, builder); } context.addYangPathArgument(arg, builder); return context; } @Nullable DataContainerCodecPrototype pathChildPrototype(final @NonNull Class argType) { return byBindingArgClass.get(argType); } @Override public final CodecContext yangPathArgumentChild(final PathArgument arg) { CodecContextSupplier supplier; if (arg instanceof NodeIdentifier nodeId) { supplier = yangChildSupplier(nodeId); } else if (arg instanceof NodeIdentifierWithPredicates nip) { supplier = yangChildSupplier(new NodeIdentifier(nip.getNodeType())); } else { supplier = null; } return childNonNull(supplier, arg, "Argument %s is not valid child of %s", arg, getSchema()).get(); } @Nullable CodecContextSupplier yangChildSupplier(final @NonNull NodeIdentifier arg) { return byYang.get(arg); } @SuppressWarnings("checkstyle:illegalCatch") final @NonNull D createBindingProxy(final DataContainerNode node) { try { return (D) proxyConstructor.invokeExact(this, node); } catch (final Throwable e) { Throwables.throwIfUnchecked(e); throw new IllegalStateException(e); } } final ValueNodeCodecContext getLeafChild(final String name) { final ValueNodeCodecContext value = leafChild.get(name); if (value == null) { throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass()); } return value; } final @NonNull ImmutableSet byYangKeySet() { return byYang.keySet(); } abstract @NonNull Map>, Augmentation> getAllAugmentationsFrom( DataContainerNode data); }