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.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
17 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
18 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
19 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
20 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
21 import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
22 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
23 import org.opendaylight.mdsal.binding.runtime.api.ContainerLikeRuntimeType;
24 import org.opendaylight.mdsal.binding.runtime.api.ContainerRuntimeType;
25 import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType;
26 import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
27 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
28 import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
29 import org.opendaylight.yangtools.yang.binding.DataObject;
30 import org.opendaylight.yangtools.yang.binding.DataRoot;
31 import org.opendaylight.yangtools.yang.binding.Identifiable;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.common.QNameModule;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
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 final class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements NodeContextSupplier {
54 private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
56 private static final VarHandle INSTANCE;
60 INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
61 "instance", DataContainerCodecContext.class);
62 } catch (NoSuchFieldException | IllegalAccessException e) {
63 throw new ExceptionInInitializerError(e);
67 private final @NonNull T type;
68 private final @NonNull QNameModule namespace;
69 private final @NonNull CodecContextFactory factory;
70 private final @NonNull Item<?> bindingArg;
71 private final @NonNull PathArgument yangArg;
72 private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
74 // Accessed via INSTANCE
75 @SuppressWarnings("unused")
76 private volatile DataContainerCodecContext<?, T> instance;
78 @SuppressWarnings("unchecked")
79 private DataContainerCodecPrototype(final Class<?> cls, final PathArgument yangArg, final T type,
80 final CodecContextFactory factory) {
81 this(Item.of((Class<? extends DataObject>) cls), yangArg, type, factory);
84 private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument yangArg, final T type,
85 final CodecContextFactory factory) {
86 this.bindingArg = requireNonNull(bindingArg);
87 this.yangArg = requireNonNull(yangArg);
88 this.type = requireNonNull(type);
89 this.factory = requireNonNull(factory);
91 if (yangArg instanceof AugmentationIdentifier augId) {
92 final var childNames = augId.getPossibleChildNames();
93 verify(!childNames.isEmpty(), "Unexpected empty identifier for %s", type);
94 namespace = childNames.iterator().next().getModule();
96 namespace = yangArg.getNodeType().getModule();
99 childAddressabilitySummary = type instanceof RuntimeType
100 ? computeChildAddressabilitySummary(((RuntimeType) type).statement())
101 // BindingRuntimeTypes, does not matter
102 : ChildAddressabilitySummary.MIXED;
105 private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
106 // FIXME: rework this to work on EffectiveStatements
107 if (nodeSchema instanceof DataNodeContainer contaner) {
108 boolean haveAddressable = false;
109 boolean haveUnaddressable = false;
110 for (DataSchemaNode child : contaner.getChildNodes()) {
111 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
112 haveAddressable = true;
113 } else if (child instanceof ListSchemaNode list) {
114 if (list.getKeyDefinition().isEmpty()) {
115 haveUnaddressable = true;
117 haveAddressable = true;
119 } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
120 || child instanceof TypedDataSchemaNode) {
121 haveUnaddressable = true;
122 } else if (child instanceof ChoiceSchemaNode choice) {
123 switch (computeChildAddressabilitySummary(choice)) {
124 case ADDRESSABLE -> haveAddressable = true;
125 case UNADDRESSABLE -> haveUnaddressable = true;
127 haveAddressable = true;
128 haveUnaddressable = true;
130 default -> throw new IllegalStateException("Unhandled accessibility summary for " + child);
133 LOG.warn("Unhandled child node {}", child);
137 if (!haveAddressable) {
138 // Empty or all are unaddressable
139 return ChildAddressabilitySummary.UNADDRESSABLE;
142 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
143 } else if (nodeSchema instanceof ChoiceSchemaNode choice) {
144 return computeChildAddressabilitySummary(choice);
147 // No child nodes possible: return unaddressable
148 return ChildAddressabilitySummary.UNADDRESSABLE;
151 private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(
152 final ChoiceSchemaNode choice) {
153 boolean haveAddressable = false;
154 boolean haveUnaddressable = false;
155 for (CaseSchemaNode child : choice.getCases()) {
156 switch (computeChildAddressabilitySummary(child)) {
158 haveAddressable = true;
161 haveUnaddressable = true;
164 // A child is mixed, which means we are mixed, too
165 return ChildAddressabilitySummary.MIXED;
167 throw new IllegalStateException("Unhandled accessibility summary for " + child);
171 if (!haveAddressable) {
172 // Empty or all are unaddressable
173 return ChildAddressabilitySummary.UNADDRESSABLE;
176 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
179 static DataContainerCodecPrototype<BindingRuntimeTypes> rootPrototype(final CodecContextFactory factory) {
180 return new DataContainerCodecPrototype<>(DataRoot.class, NodeIdentifier.create(SchemaContext.NAME),
181 factory.getRuntimeContext().getTypes(), factory);
184 static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Class<?> cls, final T type,
185 final CodecContextFactory factory) {
186 return new DataContainerCodecPrototype<>(cls, createIdentifier(type), type, factory);
189 static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T type,
190 final CodecContextFactory factory) {
191 return new DataContainerCodecPrototype<>(bindingArg, createIdentifier(type), type, factory);
194 static DataContainerCodecPrototype<AugmentRuntimeType> from(final Class<?> augClass,
195 final AugmentationIdentifier arg, final AugmentRuntimeType schema, final CodecContextFactory factory) {
196 return new DataContainerCodecPrototype<>(augClass, arg, schema, factory);
199 static DataContainerCodecPrototype<NotificationRuntimeType> from(final Class<?> augClass,
200 final NotificationRuntimeType schema, final CodecContextFactory factory) {
201 return new DataContainerCodecPrototype<>(augClass, NodeIdentifier.create(schema.statement().argument()), schema,
205 private static @NonNull NodeIdentifier createIdentifier(final CompositeRuntimeType type) {
206 final Object arg = type.statement().argument();
207 verify(arg instanceof QName, "Unexpected type %s argument %s", type, arg);
208 return NodeIdentifier.create((QName) arg);
211 @NonNull T getType() {
215 @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
216 return childAddressabilitySummary;
219 @NonNull QNameModule getNamespace() {
223 @NonNull CodecContextFactory getFactory() {
227 @NonNull Class<?> getBindingClass() {
228 return bindingArg.getType();
231 @NonNull Item<?> getBindingArg() {
235 @NonNull PathArgument getYangArg() {
240 public DataContainerCodecContext<?, T> get() {
241 final DataContainerCodecContext<?, T> existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
242 return existing != null ? existing : loadInstance();
245 private @NonNull DataContainerCodecContext<?, T> loadInstance() {
246 final var tmp = createInstance();
247 final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
248 return witness == null ? tmp : witness;
251 @SuppressWarnings({ "rawtypes", "unchecked" })
252 // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
253 private @NonNull DataContainerCodecContext<?, T> createInstance() {
254 // FIXME: make protected abstract
255 if (type instanceof ContainerLikeRuntimeType containerLike) {
256 if (containerLike instanceof ContainerRuntimeType container
257 && container.statement().findFirstEffectiveSubstatement(PresenceEffectiveStatement.class).isEmpty()) {
258 return new NonPresenceContainerNodeCodecContext(this);
260 return new ContainerNodeCodecContext(this);
261 } else if (type instanceof ListRuntimeType) {
262 return Identifiable.class.isAssignableFrom(getBindingClass())
263 ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListRuntimeType>) this)
264 : new ListNodeCodecContext(this);
265 } else if (type instanceof ChoiceRuntimeType) {
266 return new ChoiceNodeCodecContext(this);
267 } else if (type instanceof AugmentRuntimeType) {
268 return new AugmentationNodeContext(this);
269 } else if (type instanceof CaseRuntimeType) {
270 return new CaseNodeCodecContext(this);
272 throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + type);