Improve DtaContainerCodecProtype.loadInstance()
[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 com.google.common.collect.Iterables;
11 import java.lang.invoke.MethodHandles;
12 import java.lang.invoke.VarHandle;
13 import org.eclipse.jdt.annotation.NonNull;
14 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
15 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
16 import org.opendaylight.yangtools.yang.binding.DataObject;
17 import org.opendaylight.yangtools.yang.binding.DataRoot;
18 import org.opendaylight.yangtools.yang.binding.Identifiable;
19 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
20 import org.opendaylight.yangtools.yang.common.QNameModule;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
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.ContainerLike;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
34 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
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;
41
42 final class DataContainerCodecPrototype<T extends WithStatus> implements NodeContextSupplier {
43     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
44
45     private static final VarHandle INSTANCE;
46
47     static {
48         try {
49             INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
50                 "instance", DataContainerCodecContext.class);
51         } catch (NoSuchFieldException | IllegalAccessException e) {
52             throw new ExceptionInInitializerError(e);
53         }
54     }
55
56     private final T schema;
57     private final QNameModule namespace;
58     private final CodecContextFactory factory;
59     private final Item<?> bindingArg;
60     private final PathArgument yangArg;
61     private final ChildAddressabilitySummary childAddressabilitySummary;
62
63     // Accessed via INSTANCE
64     @SuppressWarnings("unused")
65     private volatile DataContainerCodecContext<?, T> instance;
66
67     @SuppressWarnings("unchecked")
68     private DataContainerCodecPrototype(final Class<?> cls, final PathArgument arg, final T nodeSchema,
69             final CodecContextFactory factory) {
70         this(Item.of((Class<? extends DataObject>) cls), arg, nodeSchema, factory);
71     }
72
73     private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument arg, final T nodeSchema,
74             final CodecContextFactory factory) {
75         this.bindingArg = bindingArg;
76         this.yangArg = arg;
77         this.schema = nodeSchema;
78         this.factory = factory;
79
80         if (arg instanceof AugmentationIdentifier) {
81             this.namespace = Iterables.getFirst(((AugmentationIdentifier) arg).getPossibleChildNames(), null)
82                     .getModule();
83         } else {
84             this.namespace = arg.getNodeType().getModule();
85         }
86
87         this.childAddressabilitySummary = computeChildAddressabilitySummary(nodeSchema);
88     }
89
90     private static ChildAddressabilitySummary computeChildAddressabilitySummary(final WithStatus nodeSchema) {
91         if (nodeSchema instanceof DataNodeContainer) {
92             boolean haveAddressable = false;
93             boolean haveUnaddressable = false;
94             for (DataSchemaNode child : ((DataNodeContainer) nodeSchema).getChildNodes()) {
95                 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
96                     haveAddressable = true;
97                 } else if (child instanceof ListSchemaNode) {
98                     if (((ListSchemaNode) child).getKeyDefinition().isEmpty()) {
99                         haveUnaddressable = true;
100                     } else {
101                         haveAddressable = true;
102                     }
103                 } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
104                         || child instanceof TypedDataSchemaNode) {
105                     haveUnaddressable = true;
106                 } else if (child instanceof ChoiceSchemaNode) {
107                     switch (computeChildAddressabilitySummary(child)) {
108                         case ADDRESSABLE:
109                             haveAddressable = true;
110                             break;
111                         case MIXED:
112                             haveAddressable = true;
113                             haveUnaddressable = true;
114                             break;
115                         case UNADDRESSABLE:
116                             haveUnaddressable = true;
117                             break;
118                         default:
119                             throw new IllegalStateException("Unhandled accessibility summary for " + child);
120                     }
121                 } else {
122                     LOG.warn("Unhandled child node {}", child);
123                 }
124             }
125
126             if (!haveAddressable) {
127                 // Empty or all are unaddressable
128                 return ChildAddressabilitySummary.UNADDRESSABLE;
129             }
130
131             return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
132         } else if (nodeSchema instanceof ChoiceSchemaNode) {
133             boolean haveAddressable = false;
134             boolean haveUnaddressable = false;
135             for (CaseSchemaNode child : ((ChoiceSchemaNode) nodeSchema).getCases()) {
136                 switch (computeChildAddressabilitySummary(child)) {
137                     case ADDRESSABLE:
138                         haveAddressable = true;
139                         break;
140                     case UNADDRESSABLE:
141                         haveUnaddressable = true;
142                         break;
143                     case MIXED:
144                         // A child is mixed, which means we are mixed, too
145                         return ChildAddressabilitySummary.MIXED;
146                     default:
147                         throw new IllegalStateException("Unhandled accessibility summary for " + child);
148                 }
149             }
150
151             if (!haveAddressable) {
152                 // Empty or all are unaddressable
153                 return ChildAddressabilitySummary.UNADDRESSABLE;
154             }
155
156             return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
157         }
158
159         // No child nodes possible: return unaddressable
160         return ChildAddressabilitySummary.UNADDRESSABLE;
161     }
162
163     static DataContainerCodecPrototype<EffectiveModelContext> rootPrototype(final CodecContextFactory factory) {
164         final EffectiveModelContext schema = factory.getRuntimeContext().getEffectiveModelContext();
165         final NodeIdentifier arg = NodeIdentifier.create(SchemaContext.NAME);
166         return new DataContainerCodecPrototype<>(DataRoot.class, arg, schema, factory);
167     }
168
169     static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Class<?> cls, final T schema,
170             final CodecContextFactory factory) {
171         return new DataContainerCodecPrototype<>(cls, NodeIdentifier.create(schema.getQName()), schema, factory);
172     }
173
174     static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T schema,
175             final CodecContextFactory factory) {
176         return new DataContainerCodecPrototype<>(bindingArg, NodeIdentifier.create(schema.getQName()), schema, factory);
177     }
178
179     static DataContainerCodecPrototype<AugmentationSchemaNode> from(final Class<?> augClass,
180             final AugmentationIdentifier arg, final AugmentationSchemaNode schema, final CodecContextFactory factory) {
181         return new DataContainerCodecPrototype<>(augClass, arg, schema, factory);
182     }
183
184     static DataContainerCodecPrototype<NotificationDefinition> from(final Class<?> augClass,
185             final NotificationDefinition schema, final CodecContextFactory factory) {
186         final PathArgument arg = NodeIdentifier.create(schema.getQName());
187         return new DataContainerCodecPrototype<>(augClass,arg, schema, factory);
188     }
189
190     T getSchema() {
191         return schema;
192     }
193
194     ChildAddressabilitySummary getChildAddressabilitySummary() {
195         return childAddressabilitySummary;
196     }
197
198     QNameModule getNamespace() {
199         return namespace;
200     }
201
202     CodecContextFactory getFactory() {
203         return factory;
204     }
205
206     Class<?> getBindingClass() {
207         return bindingArg.getType();
208     }
209
210     Item<?> getBindingArg() {
211         return bindingArg;
212     }
213
214     PathArgument getYangArg() {
215         return yangArg;
216     }
217
218     @Override
219     public DataContainerCodecContext<?, T> get() {
220         final DataContainerCodecContext<?, T> existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
221         return existing != null ? existing : loadInstance();
222     }
223
224     private @NonNull DataContainerCodecContext<?, T> loadInstance() {
225         final var tmp = createInstance();
226         final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
227         return witness == null ? tmp : witness;
228     }
229
230     @SuppressWarnings({ "rawtypes", "unchecked" })
231     // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
232     private @NonNull DataContainerCodecContext<?, T> createInstance() {
233         // FIXME: make protected abstract
234         if (schema instanceof ContainerLike) {
235             return new ContainerNodeCodecContext(this);
236         } else if (schema instanceof ListSchemaNode) {
237             return Identifiable.class.isAssignableFrom(getBindingClass())
238                     ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListSchemaNode>) this)
239                             : new ListNodeCodecContext(this);
240         } else if (schema instanceof ChoiceSchemaNode) {
241             return new ChoiceNodeCodecContext(this);
242         } else if (schema instanceof AugmentationSchemaNode) {
243             return new AugmentationNodeContext(this);
244         } else if (schema instanceof CaseSchemaNode) {
245             return new CaseNodeCodecContext(this);
246         }
247         throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + schema);
248     }
249
250     // FIXME: eliminate with above createInstance() item
251     boolean isChoice() {
252         return schema instanceof ChoiceSchemaNode;
253     }
254 }