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 com.google.common.collect.ImmutableSet;
14 import java.lang.invoke.MethodHandles;
15 import java.lang.invoke.VarHandle;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode.ChildAddressabilitySummary;
18 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
19 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
20 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
21 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
22 import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
23 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
24 import org.opendaylight.mdsal.binding.runtime.api.ContainerLikeRuntimeType;
25 import org.opendaylight.mdsal.binding.runtime.api.ContainerRuntimeType;
26 import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType;
27 import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
28 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
29 import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
30 import org.opendaylight.yangtools.yang.binding.DataObject;
31 import org.opendaylight.yangtools.yang.binding.DataRoot;
32 import org.opendaylight.yangtools.yang.binding.Identifiable;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.common.QNameModule;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
38 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
45 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
48 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.stmt.PresenceEffectiveStatement;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
53 abstract sealed class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements NodeContextSupplier {
54 static final class Augmentation extends DataContainerCodecPrototype<AugmentRuntimeType> {
55 private final @NonNull ImmutableSet<NodeIdentifier> childArgs;
57 @SuppressWarnings("unchecked")
58 Augmentation(final Class<?> cls, final QNameModule namespace, final AugmentRuntimeType type,
59 final CodecContextFactory factory, final ImmutableSet<NodeIdentifier> childArgs) {
60 super(Item.of((Class<? extends DataObject>) cls), namespace, type, factory);
61 this.childArgs = requireNonNull(childArgs);
65 PathArgument getYangArg() {
66 throw new UnsupportedOperationException("Augmentation does not have PathArgument address");
70 AugmentationNodeContext<?> createInstance() {
71 return new AugmentationNodeContext<>(this);
74 // Guaranteed to be non-empty
75 @NonNull ImmutableSet<NodeIdentifier> getChildArgs() {
80 static final class Regular<T extends RuntimeTypeContainer> extends DataContainerCodecPrototype<T> {
81 private final @NonNull PathArgument yangArg;
83 @SuppressWarnings("unchecked")
84 private Regular(final Class<?> cls, final PathArgument yangArg, final T type,
85 final CodecContextFactory factory) {
86 this(Item.of((Class<? extends DataObject>) cls), yangArg, type, factory);
89 private Regular(final Item<?> bindingArg, final PathArgument yangArg, final T type,
90 final CodecContextFactory factory) {
91 super(bindingArg, yangArg.getNodeType().getModule(), type, factory);
92 this.yangArg = requireNonNull(yangArg);
96 PathArgument getYangArg() {
101 @SuppressWarnings({ "rawtypes", "unchecked" })
102 DataContainerCodecContext<?, T> createInstance() {
103 final var type = getType();
104 if (type instanceof ContainerLikeRuntimeType containerLike) {
105 if (containerLike instanceof ContainerRuntimeType container
106 && container.statement().findFirstEffectiveSubstatement(PresenceEffectiveStatement.class)
108 return new NonPresenceContainerNodeCodecContext(this);
110 return new ContainerNodeCodecContext(this);
111 } else if (type instanceof ListRuntimeType) {
112 return Identifiable.class.isAssignableFrom(getBindingClass())
113 ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListRuntimeType>) this)
114 : new ListNodeCodecContext(this);
115 } else if (type instanceof ChoiceRuntimeType) {
116 return new ChoiceNodeCodecContext(this);
117 } else if (type instanceof CaseRuntimeType) {
118 return new CaseNodeCodecContext(this);
120 throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + type);
124 private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
126 private static final VarHandle INSTANCE;
130 INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
131 "instance", DataContainerCodecContext.class);
132 } catch (NoSuchFieldException | IllegalAccessException e) {
133 throw new ExceptionInInitializerError(e);
137 private final @NonNull T type;
138 private final @NonNull QNameModule namespace;
139 private final @NonNull CodecContextFactory factory;
140 private final @NonNull Item<?> bindingArg;
141 private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
143 // multiple paths represent augmentation wrapper
144 // FIXME: this means it is either this or 'childArgs'
146 // Accessed via INSTANCE
147 @SuppressWarnings("unused")
148 private volatile DataContainerCodecContext<?, T> instance;
150 private DataContainerCodecPrototype(final Item<?> bindingArg, final QNameModule namespace, final T type,
151 final CodecContextFactory factory) {
152 this.bindingArg = requireNonNull(bindingArg);
153 this.namespace = requireNonNull(namespace);
154 this.type = requireNonNull(type);
155 this.factory = requireNonNull(factory);
157 childAddressabilitySummary = type instanceof RuntimeType runtimeType
158 ? computeChildAddressabilitySummary(runtimeType.statement())
159 // BindingRuntimeTypes, does not matter
160 : ChildAddressabilitySummary.MIXED;
163 private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
164 // FIXME: rework this to work on EffectiveStatements
165 if (nodeSchema instanceof DataNodeContainer contaner) {
166 boolean haveAddressable = false;
167 boolean haveUnaddressable = false;
168 for (DataSchemaNode child : contaner.getChildNodes()) {
169 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
170 haveAddressable = true;
171 } else if (child instanceof ListSchemaNode list) {
172 if (list.getKeyDefinition().isEmpty()) {
173 haveUnaddressable = true;
175 haveAddressable = true;
177 } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
178 || child instanceof TypedDataSchemaNode) {
179 haveUnaddressable = true;
180 } else if (child instanceof ChoiceSchemaNode choice) {
181 switch (computeChildAddressabilitySummary(choice)) {
182 case ADDRESSABLE -> haveAddressable = true;
183 case UNADDRESSABLE -> haveUnaddressable = true;
185 haveAddressable = true;
186 haveUnaddressable = true;
188 default -> throw new IllegalStateException("Unhandled accessibility summary for " + child);
191 LOG.warn("Unhandled child node {}", child);
195 if (!haveAddressable) {
196 // Empty or all are unaddressable
197 return ChildAddressabilitySummary.UNADDRESSABLE;
200 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
201 } else if (nodeSchema instanceof ChoiceSchemaNode choice) {
202 return computeChildAddressabilitySummary(choice);
205 // No child nodes possible: return unaddressable
206 return ChildAddressabilitySummary.UNADDRESSABLE;
209 private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(
210 final ChoiceSchemaNode choice) {
211 boolean haveAddressable = false;
212 boolean haveUnaddressable = false;
213 for (CaseSchemaNode child : choice.getCases()) {
214 switch (computeChildAddressabilitySummary(child)) {
216 haveAddressable = true;
219 haveUnaddressable = true;
222 // A child is mixed, which means we are mixed, too
223 return ChildAddressabilitySummary.MIXED;
225 throw new IllegalStateException("Unhandled accessibility summary for " + child);
229 if (!haveAddressable) {
230 // Empty or all are unaddressable
231 return ChildAddressabilitySummary.UNADDRESSABLE;
234 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
237 static DataContainerCodecPrototype<BindingRuntimeTypes> rootPrototype(final CodecContextFactory factory) {
238 return new Regular<>(DataRoot.class, NodeIdentifier.create(SchemaContext.NAME),
239 factory.getRuntimeContext().getTypes(), factory);
242 static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Class<?> cls, final T type,
243 final CodecContextFactory factory) {
244 return new Regular<>(cls, createIdentifier(type), type, factory);
247 static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T type,
248 final CodecContextFactory factory) {
249 return new Regular<>(bindingArg, createIdentifier(type), type, factory);
252 static DataContainerCodecPrototype<NotificationRuntimeType> from(final Class<?> augClass,
253 final NotificationRuntimeType schema, final CodecContextFactory factory) {
254 return new Regular<>(augClass, NodeIdentifier.create(schema.statement().argument()), schema, factory);
257 private static @NonNull NodeIdentifier createIdentifier(final CompositeRuntimeType type) {
258 final Object arg = type.statement().argument();
259 verify(arg instanceof QName, "Unexpected type %s argument %s", type, arg);
260 return NodeIdentifier.create((QName) arg);
263 final @NonNull T getType() {
267 final @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
268 return childAddressabilitySummary;
271 final @NonNull QNameModule getNamespace() {
275 final @NonNull CodecContextFactory getFactory() {
279 final @NonNull Class<?> getBindingClass() {
280 return bindingArg.getType();
283 final @NonNull Item<?> getBindingArg() {
287 abstract @NonNull PathArgument getYangArg();
290 public final DataContainerCodecContext<?, T> get() {
291 final var existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
292 return existing != null ? existing : loadInstance();
295 @SuppressWarnings("unchecked")
296 final <R extends CompositeRuntimeType> DataObjectCodecContext<?, R> getDataObject() {
297 final var context = get();
298 verify(context instanceof DataObjectCodecContext, "Unexpected instance %s", context);
299 return (DataObjectCodecContext<?, R>) context;
302 private @NonNull DataContainerCodecContext<?, T> loadInstance() {
303 final var tmp = createInstance();
304 final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
305 return witness == null ? tmp : witness;
308 @SuppressWarnings({ "rawtypes", "unchecked" })
309 // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
310 abstract @NonNull DataContainerCodecContext<?, T> createInstance();