d746528b96c1dbd4ad0dcc00736f5f2e5c8cdb4e
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / CachingNormalizedNodeSerializer.java
1 /*
2  * Copyright (c) 2015 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 java.io.IOException;
13 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
14 import org.opendaylight.yangtools.yang.binding.DataObject;
15 import org.opendaylight.yangtools.yang.binding.TypeObject;
16 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
17 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 /**
22  * Serializer of Binding objects to Normalized Node which uses {@link DataObjectNormalizedNodeCache} to
23  * cache already serialized values.
24  *
25  * <p>
26  * This serializer implements {@link BindingStreamEventWriter} along with {@link BindingSerializer}.
27  *
28  * <p>
29  * {@link BindingSerializer} interface is used by generated implementations of {@link DataObjectSerializer} to provide
30  * Binding object for inspection and to prevent streaming of already serialized object.
31  */
32 final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEventWriter
33         implements BindingSerializer<Object, DataObject> {
34     private static final Logger LOG = LoggerFactory.getLogger(CachingNormalizedNodeSerializer.class);
35
36     private final NormalizedNodeResult domResult = new NormalizedNodeResult();
37     private final NormalizedNodeWriterWithAddChild domWriter = new NormalizedNodeWriterWithAddChild(domResult);
38     private final AbstractBindingNormalizedNodeCacheHolder cacheHolder;
39     private final BindingToNormalizedStreamWriter delegate;
40
41     CachingNormalizedNodeSerializer(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
42             final DataContainerCodecContext<?, ?> subtreeRoot) {
43         this.cacheHolder = requireNonNull(cacheHolder);
44         delegate = new BindingToNormalizedStreamWriter(subtreeRoot, domWriter);
45     }
46
47     @Override
48     protected AnydataBindingStreamWriter delegate() {
49         return delegate;
50     }
51
52     @Override
53     public void leafNode(final String localName, final Object value) throws IOException {
54         if (value instanceof TypeObject typed) {
55             // TypeObject is a tagging interface used for generated classes which wrap derived and restricted types.
56             // They are immutable and hence we can safely wrap them in LeafNodes and reuse them, if directed to do so.
57             final var type = typed.getClass();
58             if (cacheHolder.isCached(type)) {
59                 final var context = ((DataObjectCodecContext<?, ?>) delegate.current()).getLeafChild(localName);
60                 if (context instanceof LeafNodeCodecContext.OfTypeObject<?> typeContext) {
61                     final var cache = cacheHolder.getCachingSerializer(typeContext);
62                     if (cache != null) {
63                         // We have a cache hit and are thus done
64                         domWriter.addChild(cache.get(typed));
65                         return;
66                     }
67
68                     LOG.debug("Unexpected failure to acquire cache for context {}, skipping caching", context);
69                 } else {
70                     LOG.debug("Context {} does not match expected TypeObject {}, skipping caching", context, typed);
71                 }
72             }
73         }
74         super.leafNode(localName, value);
75     }
76
77     /**
78      * Serializes input if it is cached, returns null otherwise.
79      *
80      * <p>
81      * If input is cached it uses {@link NormalizedNodeWriterWithAddChild#addChild(NormalizedNode)}
82      * to provide already serialized value to underlying NormalizedNodeWriter in order to reuse
83      * value instead of creating new one using Normalized Node stream APIs.
84      *
85      * <p>
86      * Note that this optional is serialization of child node invoked from
87      * {@link org.opendaylight.mdsal.binding.dom.codec.impl.DataObjectSerializer}, which may opt-out from
88      * streaming of data when non-null result is returned.
89      */
90     @Override
91     public NormalizedNode serialize(final DataObject input) {
92         final var cachingSerializer = getCacheSerializer(input.implementedInterface());
93         if (cachingSerializer != null) {
94             final var domData = cachingSerializer.get(input);
95             domWriter.addChild(domData);
96             return domData;
97         }
98         return null;
99     }
100
101     private AbstractBindingNormalizedNodeCache<DataObject, ?> getCacheSerializer(
102             final Class<? extends DataObject> type) {
103         if (cacheHolder.isCached(type)) {
104             final var currentCtx = (DataContainerCodecContext<?, ?>) delegate.current();
105             if (type.equals(currentCtx.getBindingClass())) {
106                 return cacheHolder.getCachingSerializer(currentCtx);
107             }
108             return cacheHolder.getCachingSerializer(currentCtx.streamChild(type));
109         }
110         return null;
111     }
112
113     /**
114      * Serializes supplied data using stream writer with child cache enabled.
115      *
116      * @param cacheHolder Binding to Normalized Node Cache holder
117      * @param subtreeRoot Codec Node for provided data object
118      * @param data Data to be serialized
119      * @return Normalized Node representation of data.
120      */
121     static NormalizedNode serializeUsingStreamWriter(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
122             final DataContainerCodecContext<?, ?> subtreeRoot, final DataObject data) {
123         final var writer = new CachingNormalizedNodeSerializer(cacheHolder, subtreeRoot);
124         try {
125             subtreeRoot.eventStreamSerializer().serialize(data, writer);
126         } catch (final IOException e) {
127             throw new IllegalStateException(e);
128         }
129         return writer.domResult.getResult();
130     }
131 }