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.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.collect.ImmutableCollection;
14 import com.google.common.collect.ImmutableSet;
15 import java.io.IOException;
16 import java.lang.invoke.MethodHandles;
17 import java.lang.invoke.VarHandle;
18 import java.util.List;
19 import java.util.Optional;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.binding.runtime.api.BindingRuntimeContext;
24 import org.opendaylight.binding.runtime.api.ClassLoadingStrategy;
25 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
26 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
27 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
28 import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
29 import org.opendaylight.mdsal.binding.dom.codec.api.MissingClassInLoadingStrategyException;
30 import org.opendaylight.mdsal.binding.dom.codec.api.MissingSchemaException;
31 import org.opendaylight.mdsal.binding.dom.codec.api.MissingSchemaForClassException;
32 import org.opendaylight.yangtools.yang.binding.Augmentation;
33 import org.opendaylight.yangtools.yang.binding.BindingObject;
34 import org.opendaylight.yangtools.yang.binding.DataObject;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
36 import org.opendaylight.yangtools.yang.common.QName;
37 import org.opendaylight.yangtools.yang.common.QNameModule;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
42 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
43 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
44 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
46 abstract class DataContainerCodecContext<D extends DataObject, T extends WithStatus> extends NodeCodecContext
47 implements BindingDataObjectCodecTreeNode<D> {
48 private static final VarHandle EVENT_STREAM_SERIALIZER;
52 EVENT_STREAM_SERIALIZER = MethodHandles.lookup().findVarHandle(DataContainerCodecContext.class,
53 "eventStreamSerializer", DataObjectSerializer.class);
54 } catch (NoSuchFieldException | IllegalAccessException e) {
55 throw new ExceptionInInitializerError(e);
59 private final @NonNull DataContainerCodecPrototype<T> prototype;
61 // Accessed via a VarHandle
62 @SuppressWarnings("unused")
63 private volatile DataObjectSerializer eventStreamSerializer;
65 DataContainerCodecContext(final DataContainerCodecPrototype<T> prototype) {
66 this.prototype = requireNonNull(prototype);
70 public final T getSchema() {
71 return prototype.getSchema();
75 public final ChildAddressabilitySummary getChildAddressabilitySummary() {
76 return prototype.getChildAddressabilitySummary();
79 protected final QNameModule namespace() {
80 return prototype.getNamespace();
83 protected final CodecContextFactory factory() {
84 return prototype.getFactory();
88 protected YangInstanceIdentifier.PathArgument getDomPathArgument() {
89 return prototype.getYangArg();
93 * Returns nested node context using supplied YANG Instance Identifier.
95 * @param arg Yang Instance Identifier Argument
96 * @return Context of child
97 * @throws IllegalArgumentException If supplied argument does not represent valid child.
100 public abstract NodeCodecContext yangPathArgumentChild(YangInstanceIdentifier.PathArgument arg);
103 * Returns nested node context using supplied Binding Instance Identifier
104 * and adds YANG instance identifiers to supplied list.
106 * @param arg Binding Instance Identifier Argument
107 * @return Context of child or null if supplied {@code arg} does not represent valid child.
108 * @throws IllegalArgumentException If supplied argument does not represent valid child.
111 public DataContainerCodecContext<?, ?> bindingPathArgumentChild(final PathArgument arg,
112 final List<YangInstanceIdentifier.PathArgument> builder) {
113 final DataContainerCodecContext<?,?> child = streamChild(arg.getType());
114 if (builder != null) {
115 child.addYangPathArgument(arg,builder);
121 * Returns deserialized Binding Path Argument from YANG instance identifier.
123 protected PathArgument getBindingPathArgument(final YangInstanceIdentifier.PathArgument domArg) {
127 protected final PathArgument bindingArg() {
128 return prototype.getBindingArg();
131 @SuppressWarnings("unchecked")
133 public final Class<D> getBindingClass() {
134 return Class.class.cast(prototype.getBindingClass());
138 public abstract <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(Class<C> childClass);
141 * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case, one
142 * must issue getChild(ChoiceClass).getChild(CaseClass).
144 * @param childClass child class
145 * @return Context of child or Optional.empty is supplied class is not applicable in context.
148 public abstract <C extends DataObject> Optional<DataContainerCodecContext<C,?>> possibleStreamChild(
149 Class<C> childClass);
152 public String toString() {
153 return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]";
157 public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
158 final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
159 if (cacheSpecifier.isEmpty()) {
160 return new NonCachingCodec<>(this);
162 return new CachingNormalizedNodeCodec<>(this, ImmutableSet.copyOf(cacheSpecifier));
165 @NonNull BindingStreamEventWriter createWriter(final NormalizedNodeStreamWriter domWriter) {
166 return BindingToNormalizedStreamWriter.create(this, domWriter);
169 protected final <V> @NonNull V childNonNull(final @Nullable V nullable,
170 final YangInstanceIdentifier.PathArgument child, final String message, final Object... args) {
171 if (nullable == null) {
172 throw childNullException(extractName(child), message, args);
177 protected final <V> @NonNull V childNonNull(final @Nullable V nullable, final QName child, final String message,
178 final Object... args) {
179 if (nullable == null) {
180 throw childNullException(child, message, args);
185 protected final <V> @NonNull V childNonNull(final @Nullable V nullable, final Class<?> childClass,
186 final String message, final Object... args) {
187 if (nullable == null) {
188 throw childNullException(childClass, message, args);
193 private IllegalArgumentException childNullException(final QName child, final String message, final Object... args) {
194 final QNameModule module = child.getModule();
195 if (!factory().getRuntimeContext().getSchemaContext().findModule(module).isPresent()) {
196 throw new MissingSchemaException("Module " + module + " is not present in current schema context.");
198 throw IncorrectNestingException.create(message, args);
201 private IllegalArgumentException childNullException(final Class<?> childClass, final String message,
202 final Object... args) {
203 final BindingRuntimeContext runtimeContext = factory().getRuntimeContext();
204 final WithStatus schema;
205 if (Augmentation.class.isAssignableFrom(childClass)) {
206 schema = runtimeContext.getAugmentationDefinition(childClass);
208 schema = runtimeContext.getSchemaDefinition(childClass);
210 if (schema == null) {
211 throw new MissingSchemaForClassException(childClass);
214 final ClassLoadingStrategy strategy = runtimeContext.getStrategy();
216 strategy.loadClass(childClass.getName());
217 } catch (final ClassNotFoundException e) {
218 throw new MissingClassInLoadingStrategyException(
219 "User supplied class " + childClass.getName() + " is not available in " + strategy, e);
222 throw IncorrectNestingException.create(message, args);
225 private static QName extractName(final YangInstanceIdentifier.PathArgument child) {
226 if (child instanceof AugmentationIdentifier) {
227 final Set<QName> children = ((AugmentationIdentifier) child).getPossibleChildNames();
228 checkArgument(!children.isEmpty(), "Augmentation without childs must not be used in data");
229 return children.iterator().next();
231 return child.getNodeType();
234 final DataObjectSerializer eventStreamSerializer() {
235 final DataObjectSerializer existing = (DataObjectSerializer) EVENT_STREAM_SERIALIZER.getAcquire(this);
236 return existing != null ? existing : loadEventStreamSerializer();
239 // Split out to aid inlining
240 private DataObjectSerializer loadEventStreamSerializer() {
241 final DataObjectSerializer loaded = factory().getEventStreamSerializer(getBindingClass());
242 final Object witness = EVENT_STREAM_SERIALIZER.compareAndExchangeRelease(this, null, loaded);
243 return witness == null ? loaded : (DataObjectSerializer) witness;
247 public NormalizedNode<?, ?> serialize(final D data) {
248 final NormalizedNodeResult result = new NormalizedNodeResult();
249 // We create DOM stream writer which produces normalized nodes
250 final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
251 writeAsNormalizedNode(data, domWriter);
252 return result.getResult();
256 public void writeAsNormalizedNode(final D data, final NormalizedNodeStreamWriter writer) {
258 eventStreamSerializer().serialize(data, createWriter(writer));
259 } catch (final IOException e) {
260 throw new IllegalStateException("Failed to serialize Binding DTO",e);