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