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 com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
13 import java.lang.invoke.MethodHandles;
14 import java.lang.invoke.VarHandle;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode.ChildAddressabilitySummary;
17 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
18 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
19 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
20 import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
21 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
22 import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
23 import org.opendaylight.yangtools.yang.binding.DataRoot;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.common.QNameModule;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
28 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
38 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
42 abstract sealed class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements NodeContextSupplier
43 permits AugmentationCodecPrototype, DataObjectCodecPrototype {
44 private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
46 private static final VarHandle INSTANCE;
50 INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
51 "instance", DataContainerCodecContext.class);
52 } catch (NoSuchFieldException | IllegalAccessException e) {
53 throw new ExceptionInInitializerError(e);
57 private final @NonNull T type;
58 private final @NonNull QNameModule namespace;
59 private final @NonNull CodecContextFactory factory;
60 private final @NonNull Item<?> bindingArg;
61 private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
63 // multiple paths represent augmentation wrapper
64 // FIXME: this means it is either this or 'childArgs'
66 // Accessed via INSTANCE
67 @SuppressWarnings("unused")
68 private volatile DataContainerCodecContext<?, T> instance;
70 DataContainerCodecPrototype(final Item<?> bindingArg, final QNameModule namespace, final T type,
71 final CodecContextFactory factory) {
72 this.bindingArg = requireNonNull(bindingArg);
73 this.namespace = requireNonNull(namespace);
74 this.type = requireNonNull(type);
75 this.factory = requireNonNull(factory);
77 childAddressabilitySummary = type instanceof RuntimeType runtimeType
78 ? computeChildAddressabilitySummary(runtimeType.statement())
79 // BindingRuntimeTypes, does not matter
80 : ChildAddressabilitySummary.MIXED;
83 private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
84 // FIXME: rework this to work on EffectiveStatements
85 if (nodeSchema instanceof DataNodeContainer contaner) {
86 boolean haveAddressable = false;
87 boolean haveUnaddressable = false;
88 for (DataSchemaNode child : contaner.getChildNodes()) {
89 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
90 haveAddressable = true;
91 } else if (child instanceof ListSchemaNode list) {
92 if (list.getKeyDefinition().isEmpty()) {
93 haveUnaddressable = true;
95 haveAddressable = true;
97 } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
98 || child instanceof TypedDataSchemaNode) {
99 haveUnaddressable = true;
100 } else if (child instanceof ChoiceSchemaNode choice) {
101 switch (computeChildAddressabilitySummary(choice)) {
102 case ADDRESSABLE -> haveAddressable = true;
103 case UNADDRESSABLE -> haveUnaddressable = true;
105 haveAddressable = true;
106 haveUnaddressable = true;
108 default -> throw new IllegalStateException("Unhandled accessibility summary for " + child);
111 LOG.warn("Unhandled child node {}", child);
115 if (!haveAddressable) {
116 // Empty or all are unaddressable
117 return ChildAddressabilitySummary.UNADDRESSABLE;
120 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
121 } else if (nodeSchema instanceof ChoiceSchemaNode choice) {
122 return computeChildAddressabilitySummary(choice);
125 // No child nodes possible: return unaddressable
126 return ChildAddressabilitySummary.UNADDRESSABLE;
129 private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(
130 final ChoiceSchemaNode choice) {
131 boolean haveAddressable = false;
132 boolean haveUnaddressable = false;
133 for (CaseSchemaNode child : choice.getCases()) {
134 switch (computeChildAddressabilitySummary(child)) {
136 haveAddressable = true;
139 haveUnaddressable = true;
142 // A child is mixed, which means we are mixed, too
143 return ChildAddressabilitySummary.MIXED;
145 throw new IllegalStateException("Unhandled accessibility summary for " + child);
149 if (!haveAddressable) {
150 // Empty or all are unaddressable
151 return ChildAddressabilitySummary.UNADDRESSABLE;
154 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
157 static DataContainerCodecPrototype<BindingRuntimeTypes> rootPrototype(final CodecContextFactory factory) {
158 return new DataObjectCodecPrototype<>(DataRoot.class, NodeIdentifier.create(SchemaContext.NAME),
159 factory.getRuntimeContext().getTypes(), factory);
162 static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Class<?> cls, final T type,
163 final CodecContextFactory factory) {
164 return new DataObjectCodecPrototype<>(cls, createIdentifier(type), type, factory);
167 static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T type,
168 final CodecContextFactory factory) {
169 return new DataObjectCodecPrototype<>(bindingArg, createIdentifier(type), type, factory);
172 static DataContainerCodecPrototype<NotificationRuntimeType> from(final Class<?> augClass,
173 final NotificationRuntimeType schema, final CodecContextFactory factory) {
174 return new DataObjectCodecPrototype<>(augClass, NodeIdentifier.create(schema.statement().argument()), schema,
178 private static @NonNull NodeIdentifier createIdentifier(final CompositeRuntimeType type) {
179 final Object arg = type.statement().argument();
180 verify(arg instanceof QName, "Unexpected type %s argument %s", type, arg);
181 return NodeIdentifier.create((QName) arg);
184 final @NonNull T getType() {
188 final @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
189 return childAddressabilitySummary;
192 final @NonNull QNameModule getNamespace() {
196 final @NonNull CodecContextFactory getFactory() {
200 final @NonNull Class<?> getBindingClass() {
201 return bindingArg.getType();
204 final @NonNull Item<?> getBindingArg() {
208 abstract @NonNull NodeIdentifier getYangArg();
211 public final DataContainerCodecContext<?, T> get() {
212 final var existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
213 return existing != null ? existing : loadInstance();
216 @SuppressWarnings("unchecked")
217 final <R extends CompositeRuntimeType> DataObjectCodecContext<?, R> getDataObject() {
218 final var context = get();
219 verify(context instanceof DataObjectCodecContext, "Unexpected instance %s", context);
220 return (DataObjectCodecContext<?, R>) context;
223 private @NonNull DataContainerCodecContext<?, T> loadInstance() {
224 final var tmp = createInstance();
225 final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
226 return witness == null ? tmp : witness;
229 // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
230 abstract @NonNull DataContainerCodecContext<?, T> createInstance();