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