Bug 3067: Improved error reporting in Binding Data Codec
[yangtools.git] / code-generator / binding-data-codec / src / main / java / org / opendaylight / yangtools / binding / data / codec / impl / DataContainerCodecContext.java
index e873126598ba7f8cac91c29a1cbf7983c39e56ed..ef21235d59df191eefc00bb9511bc7be465ea4d6 100644 (file)
@@ -8,16 +8,30 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
 import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeCachingCodec;
 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.DataObjectSerializer;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
 
-abstract class DataContainerCodecContext<T> extends NodeCodecContext {
+abstract class DataContainerCodecContext<D extends DataObject,T> extends NodeCodecContext<D>  {
 
     private final DataContainerCodecPrototype<T> prototype;
+    private volatile DataObjectSerializer eventStreamSerializer;
 
     protected DataContainerCodecContext(final DataContainerCodecPrototype<T> prototype) {
         this.prototype = prototype;
@@ -47,23 +61,28 @@ abstract class DataContainerCodecContext<T> extends NodeCodecContext {
      * @return Context of child
      * @throws IllegalArgumentException If supplied argument does not represent valid child.
      */
-    protected abstract NodeCodecContext getYangIdentifierChild(final YangInstanceIdentifier.PathArgument arg);
+    @Override
+    public abstract NodeCodecContext<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg);
 
     /**
      * Returns nested node context using supplied Binding Instance Identifier
      * and adds YANG instance identifiers to supplied list.
      *
      * @param arg Binding Instance Identifier Argument
-     * @return Context of child
+     * @return Context of child or null if supplied {@code arg} does not represent valid child.
      * @throws IllegalArgumentException If supplied argument does not represent valid child.
      */
-    protected  DataContainerCodecContext<?> getIdentifierChild(final InstanceIdentifier.PathArgument arg,
+    @Override
+    public @Nullable DataContainerCodecContext<?,?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
             final List<YangInstanceIdentifier.PathArgument> builder) {
-        final DataContainerCodecContext<?> child = getStreamChild(arg.getType());
-        if (builder != null) {
-            child.addYangPathArgument(arg,builder);
+        final DataContainerCodecContext<?,?> child = streamChild(arg.getType());
+        if(child != null) {
+            if (builder != null) {
+                child.addYangPathArgument(arg,builder);
+            }
+            return child;
         }
-        return child;
+        throw new IllegalArgumentException("SUpplied argument is not valid child");
     }
 
     /**
@@ -80,8 +99,10 @@ abstract class DataContainerCodecContext<T> extends NodeCodecContext {
         return prototype.getBindingArg();
     }
 
-    protected final Class<?> bindingClass() {
-        return prototype.getBindingClass();
+    @SuppressWarnings("unchecked")
+    @Override
+    public final Class<D> getBindingClass() {
+        return Class.class.cast(prototype.getBindingClass());
     }
 
     /**
@@ -91,25 +112,95 @@ abstract class DataContainerCodecContext<T> extends NodeCodecContext {
      * must issue getChild(ChoiceClass).getChild(CaseClass).
      *
      * @param childClass
-     * @return Context of child
+     * @return Context of child node or null, if supplied class is not subtree child
      * @throws IllegalArgumentException If supplied child class is not valid in specified context.
      */
-    protected abstract DataContainerCodecContext<?> getStreamChild(final Class<?> childClass) throws IllegalArgumentException;
+    @Override
+    public abstract @Nullable <DV extends DataObject> DataContainerCodecContext<DV,?> streamChild(final Class<DV> childClass) throws IllegalArgumentException;
 
     /**
-    *
-    * Returns child context as if it was walked by
-    * {@link BindingStreamEventWriter}. This means that to enter case, one
-    * must issue getChild(ChoiceClass).getChild(CaseClass).
-    *
-    * @param childClass
-    * @return Context of child or Optional absent is supplied class is not applicable in context.
-    */
-   protected abstract Optional<DataContainerCodecContext<?>> getPossibleStreamChild(final Class<?> childClass);
+     *
+     * Returns child context as if it was walked by
+     * {@link BindingStreamEventWriter}. This means that to enter case, one
+     * must issue getChild(ChoiceClass).getChild(CaseClass).
+     *
+     * @param childClass
+     * @return Context of child or Optional absent is supplied class is not applicable in context.
+     */
+    @Override
+    public abstract <DV extends DataObject> Optional<DataContainerCodecContext<DV,?>> possibleStreamChild(final Class<DV> childClass);
 
     @Override
     public String toString() {
         return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]";
     }
 
+    @Override
+    public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+            final ImmutableCollection<Class<? extends DataObject>> cacheSpecifier) {
+        if(cacheSpecifier.isEmpty()) {
+            return new NonCachingCodec<>(this);
+        }
+        return new CachingNormalizedNodeCodec<D>(this,ImmutableSet.copyOf(cacheSpecifier));
+    }
+
+    BindingStreamEventWriter createWriter(final NormalizedNodeStreamWriter domWriter) {
+        return  new BindingToNormalizedStreamWriter(this, domWriter);
+    }
+
+    @Nonnull
+    protected final <V> V childNonNull(@Nullable final V nullable, final YangInstanceIdentifier.PathArgument child,
+            final String message, final Object... args) {
+        if (nullable != null) {
+            return nullable;
+        }
+        MissingSchemaException.checkModulePresent(factory().getRuntimeContext().getSchemaContext(), child);
+        throw IncorrectNestingException.create(message, args);
+    }
+
+    @Nonnull
+    protected final <V> V childNonNull(@Nullable final V nullable, final QName child, final String message,
+            final Object... args) {
+        if (nullable != null) {
+            return nullable;
+        }
+        MissingSchemaException.checkModulePresent(factory().getRuntimeContext().getSchemaContext(), child);
+        throw IncorrectNestingException.create(message, args);
+    }
+
+    @Nonnull
+    protected final <V> V childNonNull(@Nullable final V nullable, final Class<?> childClass, final String message,
+            final Object... args) {
+        if (nullable != null) {
+            return nullable;
+        }
+        MissingSchemaForClassException.check(factory().getRuntimeContext(), childClass);
+        throw IncorrectNestingException.create(message, args);
+    }
+
+    DataObjectSerializer eventStreamSerializer() {
+        if(eventStreamSerializer == null) {
+            eventStreamSerializer = factory().getEventStreamSerializer(getBindingClass());
+        }
+        return eventStreamSerializer;
+    }
+
+    @Override
+    public NormalizedNode<?, ?> serialize(final D data) {
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        // We create DOM stream writer which produces normalized nodes
+        final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        writeAsNormalizedNode(data, domWriter);
+        return result.getResult();
+    }
+
+    @Override
+    public void writeAsNormalizedNode(final D data, final NormalizedNodeStreamWriter writer) {
+        try {
+            eventStreamSerializer().serialize(data, createWriter(writer));
+        } catch (final IOException e) {
+            throw new IllegalStateException("Failed to serialize Binding DTO",e);
+        }
+    }
+
 }
\ No newline at end of file