Switch streamer instantiation to VarHandles
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / DataContainerCodecContext.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 static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableCollection;
13 import com.google.common.collect.ImmutableSet;
14 import java.io.IOException;
15 import java.lang.invoke.MethodHandles;
16 import java.lang.invoke.VarHandle;
17 import java.util.List;
18 import java.util.Optional;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
22 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
23 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
24 import org.opendaylight.yangtools.yang.binding.BindingObject;
25 import org.opendaylight.yangtools.yang.binding.DataObject;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.common.QNameModule;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
32 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
33 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
34 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
35
36 abstract class DataContainerCodecContext<D extends DataObject, T extends WithStatus> extends NodeCodecContext
37         implements BindingDataObjectCodecTreeNode<D>  {
38     private static final VarHandle EVENT_STREAM_SERIALIZER;
39
40     static {
41         try {
42             EVENT_STREAM_SERIALIZER = MethodHandles.lookup().findVarHandle(DataContainerCodecContext.class,
43                 "eventStreamSerializer", DataObjectSerializer.class);
44         } catch (NoSuchFieldException | IllegalAccessException e) {
45             throw new ExceptionInInitializerError(e);
46         }
47     }
48
49     private final @NonNull DataContainerCodecPrototype<T> prototype;
50
51     // Accessed via a VarHandle
52     @SuppressWarnings("unused")
53     private volatile DataObjectSerializer eventStreamSerializer;
54
55     DataContainerCodecContext(final DataContainerCodecPrototype<T> prototype) {
56         this.prototype = requireNonNull(prototype);
57     }
58
59     @Override
60     public final T getSchema() {
61         return prototype.getSchema();
62     }
63
64     @Override
65     public final ChildAddressabilitySummary getChildAddressabilitySummary() {
66         return prototype.getChildAddressabilitySummary();
67     }
68
69     protected final QNameModule namespace() {
70         return prototype.getNamespace();
71     }
72
73     protected final CodecContextFactory factory() {
74         return prototype.getFactory();
75     }
76
77     @Override
78     protected YangInstanceIdentifier.PathArgument getDomPathArgument() {
79         return prototype.getYangArg();
80     }
81
82     /**
83      * Returns nested node context using supplied YANG Instance Identifier.
84      *
85      * @param arg Yang Instance Identifier Argument
86      * @return Context of child
87      * @throws IllegalArgumentException If supplied argument does not represent valid child.
88      */
89     @Override
90     public abstract NodeCodecContext yangPathArgumentChild(YangInstanceIdentifier.PathArgument arg);
91
92     /**
93      * Returns nested node context using supplied Binding Instance Identifier
94      * and adds YANG instance identifiers to supplied list.
95      *
96      * @param arg Binding Instance Identifier Argument
97      * @return Context of child or null if supplied {@code arg} does not represent valid child.
98      * @throws IllegalArgumentException If supplied argument does not represent valid child.
99      */
100     @Override
101     public DataContainerCodecContext<?, ?> bindingPathArgumentChild(final PathArgument arg,
102             final List<YangInstanceIdentifier.PathArgument> builder) {
103         final DataContainerCodecContext<?,?> child = streamChild(arg.getType());
104         if (builder != null) {
105             child.addYangPathArgument(arg,builder);
106         }
107         return child;
108     }
109
110     /**
111      * Returns deserialized Binding Path Argument from YANG instance identifier.
112      */
113     protected PathArgument getBindingPathArgument(final YangInstanceIdentifier.PathArgument domArg) {
114         return bindingArg();
115     }
116
117     protected final PathArgument bindingArg() {
118         return prototype.getBindingArg();
119     }
120
121     @SuppressWarnings("unchecked")
122     @Override
123     public final Class<D> getBindingClass() {
124         return Class.class.cast(prototype.getBindingClass());
125     }
126
127     @Override
128     public abstract <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(Class<C> childClass);
129
130     /**
131      * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case, one
132      * must issue getChild(ChoiceClass).getChild(CaseClass).
133      *
134      * @param childClass child class
135      * @return Context of child or Optional.empty is supplied class is not applicable in context.
136      */
137     @Override
138     public abstract <C extends DataObject> Optional<DataContainerCodecContext<C,?>> possibleStreamChild(
139             Class<C> childClass);
140
141     @Override
142     public String toString() {
143         return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]";
144     }
145
146     @Override
147     public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
148             final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
149         if (cacheSpecifier.isEmpty()) {
150             return new NonCachingCodec<>(this);
151         }
152         return new CachingNormalizedNodeCodec<>(this, ImmutableSet.copyOf(cacheSpecifier));
153     }
154
155     BindingStreamEventWriter createWriter(final NormalizedNodeStreamWriter domWriter) {
156         return BindingToNormalizedStreamWriter.create(this, domWriter);
157     }
158
159     protected final <V> @NonNull V childNonNull(final @Nullable V nullable,
160             final YangInstanceIdentifier.PathArgument child, final String message, final Object... args) {
161         if (nullable != null) {
162             return nullable;
163         }
164         MissingSchemaException.checkModulePresent(factory().getRuntimeContext().getSchemaContext(), child);
165         throw IncorrectNestingException.create(message, args);
166     }
167
168     protected final <V> @NonNull V childNonNull(final @Nullable V nullable, final QName child, final String message,
169             final Object... args) {
170         if (nullable != null) {
171             return nullable;
172         }
173         MissingSchemaException.checkModulePresent(factory().getRuntimeContext().getSchemaContext(), child);
174         throw IncorrectNestingException.create(message, args);
175     }
176
177     protected final <V> @NonNull V childNonNull(final @Nullable V nullable, final Class<?> childClass,
178             final String message, final Object... args) {
179         if (nullable != null) {
180             return nullable;
181         }
182         MissingSchemaForClassException.check(factory().getRuntimeContext(), childClass);
183         MissingClassInLoadingStrategyException.check(factory().getRuntimeContext().getStrategy(), childClass);
184         throw IncorrectNestingException.create(message, args);
185     }
186
187     final DataObjectSerializer eventStreamSerializer() {
188         final DataObjectSerializer existing = (DataObjectSerializer) EVENT_STREAM_SERIALIZER.getAcquire(this);
189         return existing != null ? existing : loadEventStreamSerializer();
190     }
191
192     // Split out to aid inlining
193     private DataObjectSerializer loadEventStreamSerializer() {
194         final DataObjectSerializer loaded = factory().getEventStreamSerializer(getBindingClass());
195         final Object witness = EVENT_STREAM_SERIALIZER.compareAndExchangeRelease(this, null, loaded);
196         return witness == null ? loaded : (DataObjectSerializer) witness;
197     }
198
199     @Override
200     public NormalizedNode<?, ?> serialize(final D data) {
201         final NormalizedNodeResult result = new NormalizedNodeResult();
202         // We create DOM stream writer which produces normalized nodes
203         final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
204         writeAsNormalizedNode(data, domWriter);
205         return result.getResult();
206     }
207
208     @Override
209     public void writeAsNormalizedNode(final D data, final NormalizedNodeStreamWriter writer) {
210         try {
211             eventStreamSerializer().serialize(data, createWriter(writer));
212         } catch (final IOException e) {
213             throw new IllegalStateException("Failed to serialize Binding DTO",e);
214         }
215     }
216 }