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