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