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