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 static com.google.common.base.Verify.verify;
12 import java.lang.invoke.MethodHandles;
13 import java.lang.invoke.VarHandle;
14 import org.eclipse.jdt.annotation.NonNull;
15 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
16 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
17 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
18 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
19 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
20 import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
21 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
22 import org.opendaylight.mdsal.binding.runtime.api.ContainerLikeRuntimeType;
23 import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType;
24 import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
25 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
26 import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
27 import org.opendaylight.yangtools.yang.binding.DataObject;
28 import org.opendaylight.yangtools.yang.binding.DataRoot;
29 import org.opendaylight.yangtools.yang.binding.Identifiable;
30 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.common.QNameModule;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
36 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
50 final class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements NodeContextSupplier {
51 private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
53 private static final VarHandle INSTANCE;
57 INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
58 "instance", DataContainerCodecContext.class);
59 } catch (NoSuchFieldException | IllegalAccessException e) {
60 throw new ExceptionInInitializerError(e);
65 private final QNameModule namespace;
66 private final CodecContextFactory factory;
67 private final Item<?> bindingArg;
68 private final PathArgument yangArg;
69 private final ChildAddressabilitySummary childAddressabilitySummary;
71 // Accessed via INSTANCE
72 @SuppressWarnings("unused")
73 private volatile DataContainerCodecContext<?, T> instance;
75 @SuppressWarnings("unchecked")
76 private DataContainerCodecPrototype(final Class<?> cls, final PathArgument arg, final T type,
77 final CodecContextFactory factory) {
78 this(Item.of((Class<? extends DataObject>) cls), arg, type, factory);
81 private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument arg, final T type,
82 final CodecContextFactory factory) {
83 this.bindingArg = bindingArg;
86 this.factory = factory;
88 if (arg instanceof AugmentationIdentifier) {
89 final var childNames = ((AugmentationIdentifier) arg).getPossibleChildNames();
90 verify(!childNames.isEmpty(), "Unexpected empty identifier for %s", type);
91 this.namespace = childNames.iterator().next().getModule();
93 this.namespace = arg.getNodeType().getModule();
96 this.childAddressabilitySummary = type instanceof RuntimeType
97 ? computeChildAddressabilitySummary(((RuntimeType) type).statement())
98 // BindingRuntimeTypes, does not matter
99 : ChildAddressabilitySummary.MIXED;
102 private static ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
103 // FIXME: rework this to work on EffectiveStatements
104 if (nodeSchema instanceof DataNodeContainer) {
105 boolean haveAddressable = false;
106 boolean haveUnaddressable = false;
107 for (DataSchemaNode child : ((DataNodeContainer) nodeSchema).getChildNodes()) {
108 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
109 haveAddressable = true;
110 } else if (child instanceof ListSchemaNode) {
111 if (((ListSchemaNode) child).getKeyDefinition().isEmpty()) {
112 haveUnaddressable = true;
114 haveAddressable = true;
116 } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
117 || child instanceof TypedDataSchemaNode) {
118 haveUnaddressable = true;
119 } else if (child instanceof ChoiceSchemaNode) {
120 switch (computeChildAddressabilitySummary(child)) {
122 haveAddressable = true;
125 haveAddressable = true;
126 haveUnaddressable = true;
129 haveUnaddressable = true;
132 throw new IllegalStateException("Unhandled accessibility summary for " + child);
135 LOG.warn("Unhandled child node {}", child);
139 if (!haveAddressable) {
140 // Empty or all are unaddressable
141 return ChildAddressabilitySummary.UNADDRESSABLE;
144 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
145 } else if (nodeSchema instanceof ChoiceSchemaNode) {
146 boolean haveAddressable = false;
147 boolean haveUnaddressable = false;
148 for (CaseSchemaNode child : ((ChoiceSchemaNode) nodeSchema).getCases()) {
149 switch (computeChildAddressabilitySummary(child)) {
151 haveAddressable = true;
154 haveUnaddressable = true;
157 // A child is mixed, which means we are mixed, too
158 return ChildAddressabilitySummary.MIXED;
160 throw new IllegalStateException("Unhandled accessibility summary for " + child);
164 if (!haveAddressable) {
165 // Empty or all are unaddressable
166 return ChildAddressabilitySummary.UNADDRESSABLE;
169 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
172 // No child nodes possible: return unaddressable
173 return ChildAddressabilitySummary.UNADDRESSABLE;
176 static DataContainerCodecPrototype<BindingRuntimeTypes> rootPrototype(final CodecContextFactory factory) {
177 return new DataContainerCodecPrototype<>(DataRoot.class, NodeIdentifier.create(SchemaContext.NAME),
178 factory.getRuntimeContext().getTypes(), factory);
181 static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Class<?> cls, final T type,
182 final CodecContextFactory factory) {
183 return new DataContainerCodecPrototype<>(cls, createIdentifier(type), type, factory);
186 static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T type,
187 final CodecContextFactory factory) {
188 return new DataContainerCodecPrototype<>(bindingArg, createIdentifier(type), type, factory);
191 static DataContainerCodecPrototype<AugmentRuntimeType> from(final Class<?> augClass,
192 final AugmentationIdentifier arg, final AugmentRuntimeType schema, final CodecContextFactory factory) {
193 return new DataContainerCodecPrototype<>(augClass, arg, schema, factory);
196 static DataContainerCodecPrototype<NotificationRuntimeType> from(final Class<?> augClass,
197 final NotificationRuntimeType schema, final CodecContextFactory factory) {
198 final PathArgument arg = NodeIdentifier.create(schema.statement().argument());
199 return new DataContainerCodecPrototype<>(augClass, arg, schema, factory);
202 private static @NonNull NodeIdentifier createIdentifier(final CompositeRuntimeType type) {
203 final Object arg = type.statement().argument();
204 verify(arg instanceof QName, "Unexpected type %s argument %s", type, arg);
205 return NodeIdentifier.create((QName) arg);
208 @NonNull T getType() {
212 ChildAddressabilitySummary getChildAddressabilitySummary() {
213 return childAddressabilitySummary;
216 QNameModule getNamespace() {
220 CodecContextFactory getFactory() {
224 Class<?> getBindingClass() {
225 return bindingArg.getType();
228 Item<?> getBindingArg() {
232 PathArgument getYangArg() {
237 public DataContainerCodecContext<?, T> get() {
238 final DataContainerCodecContext<?, T> existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
239 return existing != null ? existing : loadInstance();
242 private @NonNull DataContainerCodecContext<?, T> loadInstance() {
243 final var tmp = createInstance();
244 final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
245 return witness == null ? tmp : witness;
248 @SuppressWarnings({ "rawtypes", "unchecked" })
249 // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
250 private @NonNull DataContainerCodecContext<?, T> createInstance() {
251 // FIXME: make protected abstract
252 if (type instanceof ContainerLikeRuntimeType) {
253 return new ContainerNodeCodecContext(this);
254 } else if (type instanceof ListRuntimeType) {
255 return Identifiable.class.isAssignableFrom(getBindingClass())
256 ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListRuntimeType>) this)
257 : new ListNodeCodecContext(this);
258 } else if (type instanceof ChoiceRuntimeType) {
259 return new ChoiceNodeCodecContext(this);
260 } else if (type instanceof AugmentRuntimeType) {
261 return new AugmentationNodeContext(this);
262 } else if (type instanceof CaseRuntimeType) {
263 return new CaseNodeCodecContext(this);
265 throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + type);