2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
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;
42 final class DataContainerCodecPrototype<T extends WithStatus> implements NodeContextSupplier {
43 private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
45 private static final VarHandle INSTANCE;
49 INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
50 "instance", DataContainerCodecContext.class);
51 } catch (NoSuchFieldException | IllegalAccessException e) {
52 throw new ExceptionInInitializerError(e);
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;
63 // Accessed via INSTANCE
64 @SuppressWarnings("unused")
65 private volatile DataContainerCodecContext<?, T> instance;
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);
73 private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument arg, final T nodeSchema,
74 final CodecContextFactory factory) {
75 this.bindingArg = bindingArg;
77 this.schema = nodeSchema;
78 this.factory = factory;
80 if (arg instanceof AugmentationIdentifier) {
81 this.namespace = Iterables.getFirst(((AugmentationIdentifier) arg).getPossibleChildNames(), null)
84 this.namespace = arg.getNodeType().getModule();
87 this.childAddressabilitySummary = computeChildAddressabilitySummary(nodeSchema);
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;
101 haveAddressable = true;
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)) {
109 haveAddressable = true;
112 haveAddressable = true;
113 haveUnaddressable = true;
116 haveUnaddressable = true;
119 throw new IllegalStateException("Unhandled accessibility summary for " + child);
122 LOG.warn("Unhandled child node {}", child);
126 if (!haveAddressable) {
127 // Empty or all are unaddressable
128 return ChildAddressabilitySummary.UNADDRESSABLE;
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)) {
138 haveAddressable = true;
141 haveUnaddressable = true;
144 // A child is mixed, which means we are mixed, too
145 return ChildAddressabilitySummary.MIXED;
147 throw new IllegalStateException("Unhandled accessibility summary for " + child);
151 if (!haveAddressable) {
152 // Empty or all are unaddressable
153 return ChildAddressabilitySummary.UNADDRESSABLE;
156 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
159 // No child nodes possible: return unaddressable
160 return ChildAddressabilitySummary.UNADDRESSABLE;
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);
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);
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);
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);
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);
194 ChildAddressabilitySummary getChildAddressabilitySummary() {
195 return childAddressabilitySummary;
198 QNameModule getNamespace() {
202 CodecContextFactory getFactory() {
206 Class<?> getBindingClass() {
207 return bindingArg.getType();
210 Item<?> getBindingArg() {
214 PathArgument getYangArg() {
219 public DataContainerCodecContext<?, T> get() {
220 final DataContainerCodecContext<?, T> existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
221 return existing != null ? existing : loadInstance();
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;
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);
247 throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + schema);
250 // FIXME: eliminate with above createInstance() item
252 return schema instanceof ChoiceSchemaNode;