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