Cache NodeContextSuppliers in CodecDataObject
[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 org.checkerframework.checker.lock.qual.Holding;
12 import org.eclipse.jdt.annotation.NonNull;
13 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
14 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
15 import org.opendaylight.yangtools.yang.binding.DataObject;
16 import org.opendaylight.yangtools.yang.binding.DataRoot;
17 import org.opendaylight.yangtools.yang.binding.Identifiable;
18 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
19 import org.opendaylight.yangtools.yang.common.QNameModule;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
23 import org.opendaylight.yangtools.yang.model.api.AnyDataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
30 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
32 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
34 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
35 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 final class DataContainerCodecPrototype<T extends WithStatus> implements NodeContextSupplier {
40     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
41
42     private final T schema;
43     private final QNameModule namespace;
44     private final CodecContextFactory factory;
45     private final Item<?> bindingArg;
46     private final PathArgument yangArg;
47     private final ChildAddressabilitySummary childAddressabilitySummary;
48
49     private volatile DataContainerCodecContext<?, T> instance;
50
51     @SuppressWarnings("unchecked")
52     private DataContainerCodecPrototype(final Class<?> cls, final PathArgument arg, final T nodeSchema,
53             final CodecContextFactory factory) {
54         this(Item.of((Class<? extends DataObject>) cls), arg, nodeSchema, factory);
55     }
56
57     private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument arg, final T nodeSchema,
58             final CodecContextFactory factory) {
59         this.bindingArg = bindingArg;
60         this.yangArg = arg;
61         this.schema = nodeSchema;
62         this.factory = factory;
63
64         if (arg instanceof AugmentationIdentifier) {
65             this.namespace = Iterables.getFirst(((AugmentationIdentifier) arg).getPossibleChildNames(), null)
66                     .getModule();
67         } else {
68             this.namespace = arg.getNodeType().getModule();
69         }
70
71         this.childAddressabilitySummary = computeChildAddressabilitySummary(nodeSchema);
72     }
73
74     private static ChildAddressabilitySummary computeChildAddressabilitySummary(final WithStatus nodeSchema) {
75         if (nodeSchema instanceof DataNodeContainer) {
76             boolean haveAddressable = false;
77             boolean haveUnaddressable = false;
78             for (DataSchemaNode child : ((DataNodeContainer) nodeSchema).getChildNodes()) {
79                 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
80                     haveAddressable = true;
81                 } else if (child instanceof ListSchemaNode) {
82                     if (((ListSchemaNode) child).getKeyDefinition().isEmpty()) {
83                         haveUnaddressable = true;
84                     } else {
85                         haveAddressable = true;
86                     }
87                 } else if (child instanceof AnyDataSchemaNode || child instanceof AnyXmlSchemaNode
88                         || child instanceof TypedDataSchemaNode) {
89                     haveUnaddressable = true;
90                 } else if (child instanceof ChoiceSchemaNode) {
91                     switch (computeChildAddressabilitySummary(child)) {
92                         case ADDRESSABLE:
93                             haveAddressable = true;
94                             break;
95                         case MIXED:
96                             haveAddressable = true;
97                             haveUnaddressable = true;
98                             break;
99                         case UNADDRESSABLE:
100                             haveUnaddressable = true;
101                             break;
102                         default:
103                             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) {
117             boolean haveAddressable = false;
118             boolean haveUnaddressable = false;
119             for (CaseSchemaNode child : ((ChoiceSchemaNode) nodeSchema).getCases().values()) {
120                 switch (computeChildAddressabilitySummary(child)) {
121                     case ADDRESSABLE:
122                         haveAddressable = true;
123                         break;
124                     case UNADDRESSABLE:
125                         haveUnaddressable = true;
126                         break;
127                     case MIXED:
128                         // A child is mixed, which means we are mixed, too
129                         return ChildAddressabilitySummary.MIXED;
130                     default:
131                         throw new IllegalStateException("Unhandled accessibility summary for " + child);
132                 }
133             }
134
135             if (!haveAddressable) {
136                 // Empty or all are unaddressable
137                 return ChildAddressabilitySummary.UNADDRESSABLE;
138             }
139
140             return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
141         }
142
143         // No child nodes possible: return unaddressable
144         return ChildAddressabilitySummary.UNADDRESSABLE;
145     }
146
147     static DataContainerCodecPrototype<SchemaContext> rootPrototype(final CodecContextFactory factory) {
148         final SchemaContext schema = factory.getRuntimeContext().getSchemaContext();
149         final NodeIdentifier arg = NodeIdentifier.create(schema.getQName());
150         return new DataContainerCodecPrototype<>(DataRoot.class, arg, schema, factory);
151     }
152
153     @SuppressWarnings({ "unchecked", "rawtypes" })
154     static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Class<?> cls, final T schema,
155             final CodecContextFactory factory) {
156         return new DataContainerCodecPrototype(cls, NodeIdentifier.create(schema.getQName()), schema, factory);
157     }
158
159     @SuppressWarnings({ "unchecked", "rawtypes" })
160     static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T schema,
161             final CodecContextFactory factory) {
162         return new DataContainerCodecPrototype(bindingArg, NodeIdentifier.create(schema.getQName()), schema, factory);
163     }
164
165     @SuppressWarnings({ "rawtypes", "unchecked" })
166     static DataContainerCodecPrototype<?> from(final Class<?> augClass, final AugmentationIdentifier arg,
167             final AugmentationSchemaNode schema, final CodecContextFactory factory) {
168         return new DataContainerCodecPrototype(augClass, arg, schema, factory);
169     }
170
171     static DataContainerCodecPrototype<NotificationDefinition> from(final Class<?> augClass,
172             final NotificationDefinition schema, final CodecContextFactory factory) {
173         final PathArgument arg = NodeIdentifier.create(schema.getQName());
174         return new DataContainerCodecPrototype<>(augClass,arg, schema, factory);
175     }
176
177     protected T getSchema() {
178         return schema;
179     }
180
181     ChildAddressabilitySummary getChildAddressabilitySummary() {
182         return childAddressabilitySummary;
183     }
184
185     protected QNameModule getNamespace() {
186         return namespace;
187     }
188
189     protected CodecContextFactory getFactory() {
190         return factory;
191     }
192
193     protected Class<?> getBindingClass() {
194         return bindingArg.getType();
195     }
196
197     protected Item<?> getBindingArg() {
198         return bindingArg;
199     }
200
201     protected PathArgument getYangArg() {
202         return yangArg;
203     }
204
205     @Override
206     public DataContainerCodecContext<?, T> get() {
207         DataContainerCodecContext<?, T> tmp = instance;
208         if (tmp == null) {
209             synchronized (this) {
210                 tmp = instance;
211                 if (tmp == null) {
212                     instance = tmp = createInstance();
213                 }
214             }
215         }
216
217         return tmp;
218     }
219
220     @Holding("this")
221     @SuppressWarnings({ "rawtypes", "unchecked" })
222     private @NonNull DataContainerCodecContext<?, T> createInstance() {
223         // FIXME: make protected abstract
224         if (schema instanceof ContainerSchemaNode) {
225             return new ContainerNodeCodecContext(this);
226         } else if (schema instanceof ListSchemaNode) {
227             return Identifiable.class.isAssignableFrom(getBindingClass())
228                     ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListSchemaNode>) this)
229                             : new ListNodeCodecContext(this);
230         } else if (schema instanceof ChoiceSchemaNode) {
231             return new ChoiceNodeCodecContext(this);
232         } else if (schema instanceof AugmentationSchemaNode) {
233             return new AugmentationNodeContext(this);
234         } else if (schema instanceof CaseSchemaNode) {
235             return new CaseNodeCodecContext(this);
236         }
237         throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + schema);
238     }
239
240     boolean isChoice() {
241         return schema instanceof ChoiceSchemaNode;
242     }
243 }