Binding2 runtime - Codecs impl - writers
[mdsal.git] / binding2 / mdsal-binding2-dom-codec / src / main / java / org / opendaylight / mdsal / binding / javav2 / dom / codec / impl / context / base / DataContainerCodecContext.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
9 package org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base;
10
11 import com.google.common.annotations.Beta;
12 import com.google.common.base.Optional;
13 import com.google.common.collect.ImmutableCollection;
14 import java.io.IOException;
15 import java.util.List;
16 import javax.annotation.Nonnull;
17 import javax.annotation.Nullable;
18 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.codecs.BindingNormalizedNodeCachingCodec;
19 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.MissingSchemaException;
20 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.serializer.BindingToNormalizedStreamWriter;
21 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
22 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
23 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
24 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializer;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.common.QNameModule;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
31 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
32 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
33 import sun.reflect.generics.reflectiveObjects.NotImplementedException;
34
35 @Beta
36 public abstract class DataContainerCodecContext<D extends TreeNode, T> extends NodeCodecContext<D> {
37
38     private final DataContainerCodecPrototype<T> prototype;
39     private volatile TreeNodeSerializer eventStreamSerializer;
40
41     protected DataContainerCodecContext(final DataContainerCodecPrototype<T> prototype) {
42         this.prototype = prototype;
43     }
44
45     @Nonnull
46     @Override
47     public final T getSchema() {
48         return prototype.getSchema();
49     }
50
51     protected final QNameModule namespace() {
52         return prototype.getNamespace();
53     }
54
55     protected final CodecContextFactory factory() {
56         return prototype.getFactory();
57     }
58
59     @Override
60     public YangInstanceIdentifier.PathArgument getDomPathArgument() {
61         return prototype.getYangArg();
62     }
63
64     /**
65      * Returns nested node context using supplied YANG Instance Identifier
66      *
67      * @param arg Yang Instance Identifier Argument
68      * @return Context of child
69      * @throws IllegalArgumentException If supplied argument does not represent valid child.
70      */
71     @Nonnull
72     @Override
73     public abstract NodeCodecContext<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg);
74
75     /**
76      * Returns nested node context using supplied Binding Instance Identifier
77      * and adds YANG instance identifiers to supplied list.
78      *
79      * @param arg Binding Instance Identifier Argument
80      * @return Context of child or null if supplied {@code arg} does not represent valid child.
81      * @throws IllegalArgumentException If supplied argument does not represent valid child.
82      */
83     @SuppressWarnings("unchecked")
84     @Nonnull
85     @Override
86     public DataContainerCodecContext<?,?> bindingPathArgumentChild(@Nonnull final TreeArgument<?> arg,
87             final List<PathArgument> builder) {
88         final DataContainerCodecContext<?,?> child = streamChild((Class<? extends TreeNode>) arg.getType());
89         if (builder != null) {
90             child.addYangPathArgument(arg,builder);
91         }
92         return child;
93     }
94
95     /**
96      * Returns de-serialized Binding Path Argument from YANG instance identifier.
97      *
98      * @param domArg input path argument
99      * @return returns binding path argument
100      */
101     @SuppressWarnings("rawtypes")
102     protected TreeArgument getBindingPathArgument(final YangInstanceIdentifier.PathArgument domArg) {
103         return bindingArg();
104     }
105
106     @SuppressWarnings("rawtypes")
107     protected final TreeArgument bindingArg() {
108         return prototype.getBindingArg();
109     }
110
111     @Nonnull
112     @SuppressWarnings("unchecked")
113     @Override
114     public final Class<D> getBindingClass() {
115         return Class.class.cast(prototype.getBindingClass());
116     }
117
118     /**
119      * Returns child context as if it was walked by
120      * {@link BindingStreamEventWriter}. This means that to enter case, one
121      * must issue getChild(ChoiceClass).getChild(CaseClass).
122      *
123      * @param childClass input child class
124      * @return Context of child node or null, if supplied class is not subtree child
125      * @throws IllegalArgumentException If supplied child class is not valid in specified context.
126      */
127     @Nonnull
128     @Override
129     public abstract <DV extends TreeNode> DataContainerCodecContext<DV,?> streamChild(@Nonnull final Class<DV> childClass) throws IllegalArgumentException;
130
131     /**
132      * Returns child context as if it was walked by
133      * {@link BindingStreamEventWriter}. This means that to enter case, one
134      * must issue getChild(ChoiceClass).getChild(CaseClass).
135      *
136      * @param childClass input child class
137      * @return Context of child or Optional absent is supplied class is not applicable in context.
138      */
139     @Override
140     public abstract <DV extends TreeNode> Optional<DataContainerCodecContext<DV, ?>> possibleStreamChild(@Nonnull
141         final Class<DV> childClass);
142
143     @Override
144     public String toString() {
145         return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]";
146     }
147
148     @Nonnull
149     @Override
150     public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
151             @Nonnull final ImmutableCollection<Class<? extends TreeNode>> cacheSpecifier) {
152
153         //TODO: implement in cache-related patches to come
154         throw new NotImplementedException();
155     }
156
157     BindingStreamEventWriter createWriter(final NormalizedNodeStreamWriter domWriter) {
158         return BindingToNormalizedStreamWriter.create(this, domWriter);
159     }
160
161     @Nonnull
162     protected final <V> V childNonNull(@Nullable final V nullable, final YangInstanceIdentifier.PathArgument child,
163             final String message, final Object... args) {
164         if (nullable != null) {
165             return nullable;
166         }
167         MissingSchemaException.checkModulePresent(factory().getRuntimeContext().getSchemaContext(), child);
168         throw IncorrectNestingException.create(message, args);
169     }
170
171     @Nonnull
172     protected final <V> V childNonNull(@Nullable final V nullable, final QName child, final String message,
173             final Object... args) {
174         if (nullable != null) {
175             return nullable;
176         }
177         MissingSchemaException.checkModulePresent(factory().getRuntimeContext().getSchemaContext(), child);
178         throw IncorrectNestingException.create(message, args);
179     }
180
181     @Nonnull
182     protected final <V> V childNonNull(@Nullable final V nullable, final Class<?> childClass, final String message,
183             final Object... args) {
184         if (nullable != null) {
185             return nullable;
186         }
187         MissingSchemaForClassException.check(factory().getRuntimeContext(), childClass);
188         MissingClassInLoadingStrategyException.check(factory().getRuntimeContext().getStrategy(), childClass);
189         throw IncorrectNestingException.create(message, args);
190     }
191
192     public TreeNodeSerializer eventStreamSerializer() {
193         if(eventStreamSerializer == null) {
194             eventStreamSerializer = factory().getEventStreamSerializer(getBindingClass());
195         }
196         return eventStreamSerializer;
197     }
198
199     @Nonnull
200     @Override
201     public NormalizedNode<?, ?> serialize(@Nonnull final D data) {
202         final NormalizedNodeResult result = new NormalizedNodeResult();
203         // We create DOM stream writer which produces normalized nodes
204         final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
205         writeAsNormalizedNode(data, domWriter);
206         return result.getResult();
207     }
208
209     @Override
210     public void writeAsNormalizedNode(final D data, final NormalizedNodeStreamWriter writer) {
211         try {
212             eventStreamSerializer().serialize(data, createWriter(writer));
213         } catch (final IOException e) {
214             throw new IllegalStateException("Failed to serialize Binding DTO",e);
215         }
216     }
217
218 }