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