Reparent ChoiceCodecContext
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / CachingNormalizedNodeSerializer.java
index d8f08d96b71bea022a01e756e0c286fcc307e823..60cbccf998b45c4acee59fb22092cc680a97d09d 100644 (file)
@@ -7,48 +7,90 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
+import static java.util.Objects.requireNonNull;
+
 import java.io.IOException;
-import org.opendaylight.yangtools.yang.binding.BindingSerializer;
-import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
 import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.TypeObject;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * Serializer of Binding objects to Normalized Node which uses {@link BindingNormalizedNodeCache} to
+ * Serializer of Binding objects to Normalized Node which uses {@link DataObjectNormalizedNodeCache} to
  * cache already serialized values.
  *
  * <p>
  * This serializer implements {@link BindingStreamEventWriter} along with {@link BindingSerializer}.
  *
  * <p>
- * {@link BindingSerializer} interface is used by generated implementations of
- * {@link org.opendaylight.yangtools.yang.binding.DataObjectSerializer} to provide Binding object
- * for inspection and to prevent streaming of already serialized object.
+ * {@link BindingSerializer} interface is used by generated implementations of {@link DataContainerSerializer} to
+ * provide Binding object for inspection and to prevent streaming of already serialized object.
  */
-final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEventWriter implements
-        BindingSerializer<Object, DataObject> {
+final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEventWriter
+        implements BindingSerializer<Object, DataObject> {
+    private static final Logger LOG = LoggerFactory.getLogger(CachingNormalizedNodeSerializer.class);
 
-    private final NormalizedNodeResult domResult;
-    private final NormalizedNodeWriterWithAddChild domWriter;
-    private final BindingToNormalizedStreamWriter delegate;
+    private final NormalizationResultHolder domResult = new NormalizationResultHolder();
+    private final NormalizedNodeWriterWithAddChild domWriter = new NormalizedNodeWriterWithAddChild(domResult);
     private final AbstractBindingNormalizedNodeCacheHolder cacheHolder;
+    private final BindingToNormalizedStreamWriter delegate;
 
-    CachingNormalizedNodeSerializer(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
-            final DataContainerCodecContext<?, ?> subtreeRoot) {
-        this.cacheHolder = cacheHolder;
-        this.domResult = new NormalizedNodeResult();
-        this.domWriter = new NormalizedNodeWriterWithAddChild(domResult);
-        this.delegate = BindingToNormalizedStreamWriter.create(subtreeRoot, domWriter);
+    private CachingNormalizedNodeSerializer(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
+            final DataContainerCodecContext<?, ?, ?> subtreeRoot) {
+        this.cacheHolder = requireNonNull(cacheHolder);
+        delegate = new BindingToNormalizedStreamWriter(subtreeRoot, domWriter);
+    }
+
+    /**
+     * Serializes supplied data using stream writer with child cache enabled.
+     *
+     * @param cacheHolder Binding to Normalized Node Cache holder
+     * @param subtreeRoot Codec Node for provided data object
+     * @param data Data to be serialized
+     * @return Normalized Node representation of data.
+     */
+    static NormalizedNode serializeUsingStreamWriter(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
+            final DataContainerCodecContext<?, ?, ?> subtreeRoot, final DataObject data) {
+        final var writer = new CachingNormalizedNodeSerializer(cacheHolder, subtreeRoot);
+        try {
+            subtreeRoot.eventStreamSerializer().serialize(data, writer);
+        } catch (final IOException e) {
+            throw new IllegalStateException(e);
+        }
+        return writer.domResult.getResult().data();
     }
 
     @Override
-    protected BindingStreamEventWriter delegate() {
+    protected AnydataBindingStreamWriter delegate() {
         return delegate;
     }
 
-    NormalizedNode<?, ?> build() {
-        return domResult.getResult();
+    @Override
+    public void leafNode(final String localName, final Object value) throws IOException {
+        if (value instanceof TypeObject typed) {
+            // TypeObject is a tagging interface used for generated classes which wrap derived and restricted types.
+            // They are immutable and hence we can safely wrap them in LeafNodes and reuse them, if directed to do so.
+            final var type = typed.getClass();
+            if (cacheHolder.isCached(type)) {
+                final var context = ((AbstractDataObjectCodecContext<?, ?>) delegate.current()).getLeafChild(localName);
+                if (context instanceof LeafNodeCodecContext.OfTypeObject<?> typeContext) {
+                    final var cache = cacheHolder.getCachingSerializer(typeContext);
+                    if (cache != null) {
+                        // We have a cache hit and are thus done
+                        domWriter.addChild(cache.get(typed));
+                        return;
+                    }
+
+                    LOG.debug("Unexpected failure to acquire cache for context {}, skipping caching", context);
+                } else {
+                    LOG.debug("Context {} does not match expected TypeObject {}, skipping caching", context, typed);
+                }
+            }
+        }
+        super.leafNode(localName, value);
     }
 
     /**
@@ -61,66 +103,29 @@ final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEvent
      *
      * <p>
      * Note that this optional is serialization of child node invoked from
-     * {@link org.opendaylight.yangtools.yang.binding.DataObjectSerializer}, which may opt-out from
+     * {@link org.opendaylight.mdsal.binding.dom.codec.impl.DataContainerSerializer}, which may opt-out from
      * streaming of data when non-null result is returned.
      */
     @Override
-    public NormalizedNode<?, ?> serialize(final DataObject input) {
-        final BindingNormalizedNodeCache cachingSerializer = getCacheSerializer(input.getImplementedInterface());
+    public NormalizedNode serialize(final DataObject input) {
+        final var cachingSerializer = getCacheSerializer(input.implementedInterface());
         if (cachingSerializer != null) {
-            final NormalizedNode<?, ?> domData = cachingSerializer.get(input);
+            final var domData = cachingSerializer.get(input);
             domWriter.addChild(domData);
             return domData;
         }
         return null;
     }
 
-    /**
-     * Serializes supplied data using stream writer with child cache enabled or using cache directly
-     * if cache is avalaible also for supplied Codec node.
-     *
-     * @param cacheHolder Binding to Normalized Node Cache holder
-     * @param subtreeRoot Codec Node for provided data object
-     * @param data Data to be serialized
-     * @return Normalized Node representation of data.
-     */
-    static NormalizedNode<?, ?> serialize(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
-            final DataContainerCodecContext<?, ?> subtreeRoot, final DataObject data) {
-        final BindingNormalizedNodeCache cache = cacheHolder.getCachingSerializer(subtreeRoot);
-        if (cache != null) {
-            return cache.get(data);
-        }
-        return serializeUsingStreamWriter(cacheHolder, subtreeRoot, data);
-    }
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    private BindingNormalizedNodeCache getCacheSerializer(final Class type) {
+    private AbstractBindingNormalizedNodeCache<DataObject, ?> getCacheSerializer(
+            final Class<? extends DataObject> type) {
         if (cacheHolder.isCached(type)) {
-            final DataContainerCodecContext<?, ?> currentCtx = (DataContainerCodecContext<?, ?>) delegate.current();
+            final var currentCtx = (DataContainerCodecContext<?, ?, ?>) delegate.current();
             if (type.equals(currentCtx.getBindingClass())) {
                 return cacheHolder.getCachingSerializer(currentCtx);
             }
-            return cacheHolder.getCachingSerializer(currentCtx.streamChild(type));
+            return cacheHolder.getCachingSerializer(currentCtx.getStreamChild(type));
         }
         return null;
     }
-
-    /**
-     * Serializes supplied data using stream writer with child cache enabled.
-     *
-     * @param cacheHolder Binding to Normalized Node Cache holder
-     * @param subtreeRoot Codec Node for provided data object
-     * @param data Data to be serialized
-     * @return Normalized Node representation of data.
-     */
-    static NormalizedNode<?, ?> serializeUsingStreamWriter(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
-            final DataContainerCodecContext<?, ?> subtreeRoot, final DataObject data) {
-        final CachingNormalizedNodeSerializer writer = new CachingNormalizedNodeSerializer(cacheHolder, subtreeRoot);
-        try {
-            subtreeRoot.eventStreamSerializer().serialize(data, writer);
-            return writer.build();
-        } catch (final IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
\ No newline at end of file
+}