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