2 * Copyright (c) 2014 Cisco Systems, Inc. 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 java.lang.invoke.MethodHandles;
13 import java.lang.invoke.VarHandle;
14 import org.eclipse.jdt.annotation.NonNull;
15 import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode.ChildAddressabilitySummary;
16 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
17 import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
18 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
19 import org.opendaylight.yangtools.yang.common.QNameModule;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
21 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
28 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 abstract sealed class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements CodecContextSupplier
35 permits AugmentationCodecPrototype, DataObjectCodecPrototype {
36 private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
38 private static final VarHandle INSTANCE;
42 INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
43 "instance", DataContainerCodecContext.class);
44 } catch (NoSuchFieldException | IllegalAccessException e) {
45 throw new ExceptionInInitializerError(e);
49 private final @NonNull T type;
50 private final @NonNull QNameModule namespace;
51 private final @NonNull CodecContextFactory factory;
52 private final @NonNull Item<?> bindingArg;
53 private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
55 // multiple paths represent augmentation wrapper
56 // FIXME: this means it is either this or 'childArgs'
58 // Accessed via INSTANCE
59 @SuppressWarnings("unused")
60 private volatile DataContainerCodecContext<?, T> instance;
62 DataContainerCodecPrototype(final Item<?> bindingArg, final QNameModule namespace, final T type,
63 final CodecContextFactory factory) {
64 this.bindingArg = requireNonNull(bindingArg);
65 this.namespace = requireNonNull(namespace);
66 this.type = requireNonNull(type);
67 this.factory = requireNonNull(factory);
69 childAddressabilitySummary = type instanceof RuntimeType runtimeType
70 ? computeChildAddressabilitySummary(runtimeType.statement())
71 // BindingRuntimeTypes, does not matter
72 : ChildAddressabilitySummary.MIXED;
75 private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
76 // FIXME: rework this to work on EffectiveStatements
77 if (nodeSchema instanceof DataNodeContainer contaner) {
78 boolean haveAddressable = false;
79 boolean haveUnaddressable = false;
80 for (DataSchemaNode child : contaner.getChildNodes()) {
81 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
82 haveAddressable = true;
83 } else if (child instanceof ListSchemaNode list) {
84 if (list.getKeyDefinition().isEmpty()) {
85 haveUnaddressable = true;
87 haveAddressable = true;
89 } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
90 || child instanceof TypedDataSchemaNode) {
91 haveUnaddressable = true;
92 } else if (child instanceof ChoiceSchemaNode choice) {
93 switch (computeChildAddressabilitySummary(choice)) {
94 case ADDRESSABLE -> haveAddressable = true;
95 case UNADDRESSABLE -> haveUnaddressable = true;
97 haveAddressable = true;
98 haveUnaddressable = true;
100 default -> throw new IllegalStateException("Unhandled accessibility summary for " + child);
103 LOG.warn("Unhandled child node {}", child);
107 if (!haveAddressable) {
108 // Empty or all are unaddressable
109 return ChildAddressabilitySummary.UNADDRESSABLE;
112 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
113 } else if (nodeSchema instanceof ChoiceSchemaNode choice) {
114 return computeChildAddressabilitySummary(choice);
117 // No child nodes possible: return unaddressable
118 return ChildAddressabilitySummary.UNADDRESSABLE;
121 private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(
122 final ChoiceSchemaNode choice) {
123 boolean haveAddressable = false;
124 boolean haveUnaddressable = false;
125 for (CaseSchemaNode child : choice.getCases()) {
126 switch (computeChildAddressabilitySummary(child)) {
128 haveAddressable = true;
131 haveUnaddressable = true;
134 // A child is mixed, which means we are mixed, too
135 return ChildAddressabilitySummary.MIXED;
137 throw new IllegalStateException("Unhandled accessibility summary for " + child);
141 if (!haveAddressable) {
142 // Empty or all are unaddressable
143 return ChildAddressabilitySummary.UNADDRESSABLE;
146 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
149 final @NonNull T getType() {
153 final @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
154 return childAddressabilitySummary;
157 final @NonNull QNameModule getNamespace() {
161 final @NonNull CodecContextFactory getFactory() {
165 final @NonNull Class<?> getBindingClass() {
166 return bindingArg.getType();
169 final @NonNull Item<?> getBindingArg() {
173 abstract @NonNull NodeIdentifier getYangArg();
176 public final DataContainerCodecContext<?, T> get() {
177 final var existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
178 return existing != null ? existing : loadInstance();
181 private @NonNull DataContainerCodecContext<?, T> loadInstance() {
182 final var tmp = createInstance();
183 final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
184 return witness == null ? tmp : witness;
187 // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
188 abstract @NonNull DataContainerCodecContext<?, T> createInstance();