Rename NodeCodecContext
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / DataContainerCodecPrototype.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
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.runtime.api.CompositeRuntimeType;
18 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
19 import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
20 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.common.QNameModule;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
24 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 abstract sealed class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements CodecContextSupplier
38         permits AugmentationCodecPrototype, DataObjectCodecPrototype {
39     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
40
41     private static final VarHandle INSTANCE;
42
43     static {
44         try {
45             INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
46                 "instance", DataContainerCodecContext.class);
47         } catch (NoSuchFieldException | IllegalAccessException e) {
48             throw new ExceptionInInitializerError(e);
49         }
50     }
51
52     private final @NonNull T type;
53     private final @NonNull QNameModule namespace;
54     private final @NonNull CodecContextFactory factory;
55     private final @NonNull Item<?> bindingArg;
56     private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
57
58     // multiple paths represent augmentation wrapper
59     // FIXME: this means it is either this or 'childArgs'
60
61     // Accessed via INSTANCE
62     @SuppressWarnings("unused")
63     private volatile DataContainerCodecContext<?, T> instance;
64
65     DataContainerCodecPrototype(final Item<?> bindingArg, final QNameModule namespace, final T type,
66             final CodecContextFactory factory) {
67         this.bindingArg = requireNonNull(bindingArg);
68         this.namespace = requireNonNull(namespace);
69         this.type = requireNonNull(type);
70         this.factory = requireNonNull(factory);
71
72         childAddressabilitySummary = type instanceof RuntimeType runtimeType
73             ? computeChildAddressabilitySummary(runtimeType.statement())
74                 // BindingRuntimeTypes, does not matter
75                 : ChildAddressabilitySummary.MIXED;
76     }
77
78     private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
79         // FIXME: rework this to work on EffectiveStatements
80         if (nodeSchema instanceof DataNodeContainer contaner) {
81             boolean haveAddressable = false;
82             boolean haveUnaddressable = false;
83             for (DataSchemaNode child : contaner.getChildNodes()) {
84                 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
85                     haveAddressable = true;
86                 } else if (child instanceof ListSchemaNode list) {
87                     if (list.getKeyDefinition().isEmpty()) {
88                         haveUnaddressable = true;
89                     } else {
90                         haveAddressable = true;
91                     }
92                 } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
93                         || child instanceof TypedDataSchemaNode) {
94                     haveUnaddressable = true;
95                 } else if (child instanceof ChoiceSchemaNode choice) {
96                     switch (computeChildAddressabilitySummary(choice)) {
97                         case ADDRESSABLE -> haveAddressable = true;
98                         case UNADDRESSABLE -> haveUnaddressable = true;
99                         case MIXED -> {
100                             haveAddressable = true;
101                             haveUnaddressable = true;
102                         }
103                         default -> throw new IllegalStateException("Unhandled accessibility summary for " + child);
104                     }
105                 } else {
106                     LOG.warn("Unhandled child node {}", child);
107                 }
108             }
109
110             if (!haveAddressable) {
111                 // Empty or all are unaddressable
112                 return ChildAddressabilitySummary.UNADDRESSABLE;
113             }
114
115             return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
116         } else if (nodeSchema instanceof ChoiceSchemaNode choice) {
117             return computeChildAddressabilitySummary(choice);
118         }
119
120         // No child nodes possible: return unaddressable
121         return ChildAddressabilitySummary.UNADDRESSABLE;
122     }
123
124     private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(
125             final ChoiceSchemaNode choice) {
126         boolean haveAddressable = false;
127         boolean haveUnaddressable = false;
128         for (CaseSchemaNode child : choice.getCases()) {
129             switch (computeChildAddressabilitySummary(child)) {
130                 case ADDRESSABLE:
131                     haveAddressable = true;
132                     break;
133                 case UNADDRESSABLE:
134                     haveUnaddressable = true;
135                     break;
136                 case MIXED:
137                     // A child is mixed, which means we are mixed, too
138                     return ChildAddressabilitySummary.MIXED;
139                 default:
140                     throw new IllegalStateException("Unhandled accessibility summary for " + child);
141             }
142         }
143
144         if (!haveAddressable) {
145             // Empty or all are unaddressable
146             return ChildAddressabilitySummary.UNADDRESSABLE;
147         }
148
149         return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
150     }
151
152     static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Class<?> cls, final T type,
153             final CodecContextFactory factory) {
154         return new DataObjectCodecPrototype<>(cls, createIdentifier(type), type, factory);
155     }
156
157     static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T type,
158             final CodecContextFactory factory) {
159         return new DataObjectCodecPrototype<>(bindingArg, createIdentifier(type), type, factory);
160     }
161
162     private static @NonNull NodeIdentifier createIdentifier(final CompositeRuntimeType type) {
163         final Object arg = type.statement().argument();
164         verify(arg instanceof QName, "Unexpected type %s argument %s", type, arg);
165         return NodeIdentifier.create((QName) arg);
166     }
167
168     final @NonNull T getType() {
169         return type;
170     }
171
172     final @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
173         return childAddressabilitySummary;
174     }
175
176     final @NonNull QNameModule getNamespace() {
177         return namespace;
178     }
179
180     final @NonNull CodecContextFactory getFactory() {
181         return factory;
182     }
183
184     final @NonNull Class<?> getBindingClass() {
185         return bindingArg.getType();
186     }
187
188     final @NonNull Item<?> getBindingArg() {
189         return bindingArg;
190     }
191
192     abstract @NonNull NodeIdentifier getYangArg();
193
194     @Override
195     public final DataContainerCodecContext<?, T> get() {
196         final var existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
197         return existing != null ? existing : loadInstance();
198     }
199
200     @SuppressWarnings("unchecked")
201     final <R extends CompositeRuntimeType> DataObjectCodecContext<?, R> getDataObject() {
202         final var context = get();
203         verify(context instanceof DataObjectCodecContext, "Unexpected instance %s", context);
204         return (DataObjectCodecContext<?, R>) context;
205     }
206
207     private @NonNull DataContainerCodecContext<?, T> loadInstance() {
208         final var tmp = createInstance();
209         final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
210         return witness == null ? tmp : witness;
211     }
212
213     // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
214     abstract @NonNull DataContainerCodecContext<?, T> createInstance();
215 }