86049f1011b09f1dd7fc5b6883d83007298b3bc2
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / DataContainerStreamer.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, 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.dom.codec.impl;
9
10 import static com.google.common.base.Verify.verify;
11
12 import com.google.common.annotations.Beta;
13 import java.io.IOException;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
19 import org.opendaylight.yangtools.yang.binding.Augmentable;
20 import org.opendaylight.yangtools.yang.binding.Augmentation;
21 import org.opendaylight.yangtools.yang.binding.DataContainer;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.KeyAware;
24 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * Base superclass for all concrete streamers, that is objects which are able to turn a concrete DataObject into a
30  * stream of events.
31  *
32  * @param <T> DataContainer type
33  */
34 @Beta
35 public abstract class DataContainerStreamer<T extends DataContainer> {
36     private static final Logger LOG = LoggerFactory.getLogger(DataContainerStreamer.class);
37
38     protected DataContainerStreamer() {
39
40     }
41
42     /**
43      * Writes stream events for supplied data object to provided stream.
44      *
45      * <p>
46      * DataObjectSerializerRegistry may be used to lookup serializers for other generated classes  in order to support
47      * writing their events.
48      */
49     protected abstract void serialize(DataContainerSerializerRegistry reg, DataContainer obj,
50         BindingStreamEventWriter stream) throws IOException;
51
52     protected static final void streamAnydata(final BindingStreamEventWriter writer, final String localName,
53             final Object value) throws IOException {
54         if (value != null && writer instanceof AnydataBindingStreamWriter anydataWriter) {
55             verify(value instanceof OpaqueObject, "Unexpected data %s", value);
56             anydataWriter.anydataNode(localName, (OpaqueObject<?>) value);
57         }
58     }
59
60     protected static final void streamAnyxml(final BindingStreamEventWriter writer, final String localName,
61             final Object value) throws IOException {
62         if (value != null) {
63             writer.anyxmlNode(localName, value);
64         }
65     }
66
67     protected static final void streamAugmentations(final DataContainerSerializerRegistry registry,
68             final BindingStreamEventWriter writer, final Augmentable<?> obj) throws IOException {
69         for (final var aug : obj.augmentations().entrySet()) {
70             emitAugmentation(aug.getKey(), aug.getValue(), writer, registry);
71         }
72     }
73
74     protected static final <C extends DataContainer> void streamChoice(final Class<C> choiceClass,
75             final DataContainerSerializerRegistry registry, final BindingStreamEventWriter writer, final C value)
76                     throws IOException {
77         if (value != null) {
78             final Class<? extends DataContainer> caseClass = value.implementedInterface();
79             writer.startChoiceNode(choiceClass, BindingStreamEventWriter.UNKNOWN_SIZE);
80             final DataContainerSerializer caseStreamer = registry.getSerializer(caseClass.asSubclass(DataObject.class));
81             if (caseStreamer != null) {
82                 if (tryCache(writer, (DataObject) value)) {
83                     caseStreamer.serialize((DataObject) value, writer);
84                 }
85             } else {
86                 LOG.warn("No serializer for case {} is available in registry {}", caseClass, registry);
87             }
88
89             writer.endNode();
90         }
91     }
92
93     protected static final <C extends DataObject> void streamContainer(final DataContainerStreamer<C> childStreamer,
94             final DataContainerSerializerRegistry registry, final BindingStreamEventWriter writer, final C value)
95                     throws IOException {
96         if (value != null && tryCache(writer, value)) {
97             childStreamer.serialize(registry, value, writer);
98         }
99     }
100
101     protected static final void streamLeaf(final BindingStreamEventWriter writer, final String localName,
102             final Object value) throws IOException {
103         if (value != null) {
104             writer.leafNode(localName, value);
105         }
106     }
107
108     protected static final void streamLeafList(final BindingStreamEventWriter writer, final String localName,
109             final Set<?> value) throws IOException {
110         if (value != null) {
111             writer.startLeafSet(localName, value.size());
112             commonStreamLeafset(writer, value);
113         }
114     }
115
116     protected static final void streamOrderedLeafList(final BindingStreamEventWriter writer,
117             final String localName, final List<?> value) throws IOException {
118         if (value != null) {
119             writer.startOrderedLeafSet(localName, value.size());
120             commonStreamLeafset(writer, value);
121         }
122     }
123
124     protected static final <E extends DataObject> void streamList(final Class<E> childClass,
125             final DataContainerStreamer<E> childStreamer, final DataContainerSerializerRegistry registry,
126             final BindingStreamEventWriter writer, final List<? extends E> value) throws IOException {
127         final int size = nullSize(value);
128         if (size != 0) {
129             writer.startUnkeyedList(childClass, size);
130             commonStreamList(registry, writer, childStreamer, value);
131         }
132     }
133
134     protected static final <E extends DataObject & KeyAware<?>> void streamMap(final Class<E> childClass,
135             final DataContainerStreamer<E> childStreamer, final DataContainerSerializerRegistry registry,
136             final BindingStreamEventWriter writer, final Map<?, ? extends E> value) throws IOException {
137         final int size = nullSize(value);
138         if (size != 0) {
139             writer.startMapNode(childClass, size);
140             commonStreamList(registry, writer, childStreamer, value.values());
141         }
142     }
143
144     protected static final <E extends DataObject & KeyAware<?>> void streamOrderedMap(final Class<E> childClass,
145             final DataContainerStreamer<E> childStreamer, final DataContainerSerializerRegistry registry,
146             final BindingStreamEventWriter writer, final List<? extends E> value) throws IOException {
147         final int size = nullSize(value);
148         if (size != 0) {
149             writer.startOrderedMapNode(childClass, size);
150             commonStreamList(registry, writer, childStreamer, value);
151         }
152     }
153
154     private static <E extends DataObject> void commonStreamList(final DataContainerSerializerRegistry registry,
155             final BindingStreamEventWriter writer, final DataContainerStreamer<E> childStreamer,
156             final Collection<? extends E> value) throws IOException {
157
158         for (E entry : value) {
159             if (tryCache(writer, entry)) {
160                 childStreamer.serialize(registry, entry, writer);
161             }
162         }
163         writer.endNode();
164     }
165
166     private static void commonStreamLeafset(final BindingStreamEventWriter writer, final Collection<?> value)
167             throws IOException {
168         for (Object entry : value) {
169             writer.leafSetEntryNode(entry);
170         }
171         writer.endNode();
172     }
173
174     private static void emitAugmentation(final Class<? extends Augmentation<?>> type, final Augmentation<?> value,
175             final BindingStreamEventWriter writer, final DataContainerSerializerRegistry registry) throws IOException {
176         /*
177          * Binding Specification allowed to insert augmentation with null for value, which effectively could be used to
178          * remove augmentation from builder / DTO.
179          */
180         if (value != null) {
181             final DataContainerSerializer serializer = registry.getSerializer(type);
182             if (serializer != null) {
183                 serializer.serialize(value, writer);
184             } else {
185                 LOG.warn("DataObjectSerializer is not present for {} in registry {}", type, registry);
186             }
187         }
188     }
189
190     @SuppressWarnings("unchecked")
191     private static <T extends DataObject> boolean tryCache(final BindingStreamEventWriter writer, final T value) {
192         // Force serialization if writer is not a BindingSerializer, otherwise defer to it for a decision
193         return !(writer instanceof BindingSerializer) || ((BindingSerializer<?, T>) writer).serialize(value) == null;
194     }
195
196     private static int nullSize(final Collection<?> list) {
197         return list == null ? 0 : list.size();
198     }
199
200     private static int nullSize(final Map<?, ?> map) {
201         return map == null ? 0 : map.size();
202     }
203 }