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 / ChoiceNodeCodecContext.java
index bac768703c24cab5f00e1b13014143f9b5fcf218..7ab7e6b346e0dd972b9727ddbfb4f02f9e716afa 100644 (file)
@@ -11,52 +11,56 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
-
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceNode> {
+final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCodecContext<D,ChoiceSchemaNode> {
     private static final Logger LOG = LoggerFactory.getLogger(ChoiceNodeCodecContext.class);
     private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChild;
     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byClass;
     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClass;
 
-    public ChoiceNodeCodecContext(final DataContainerCodecPrototype<ChoiceNode> prototype) {
+    public ChoiceNodeCodecContext(final DataContainerCodecPrototype<ChoiceSchemaNode> prototype) {
         super(prototype);
-        Map<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChildBuilder = new HashMap<>();
-        Map<Class<?>, DataContainerCodecPrototype<?>> byClassBuilder = new HashMap<>();
-        Map<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClassBuilder = new HashMap<>();
-        Set<Class<?>> potentialSubstitutions = new HashSet<>();
+        final Map<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChildBuilder = new HashMap<>();
+        final Map<Class<?>, DataContainerCodecPrototype<?>> byClassBuilder = new HashMap<>();
+        final Map<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClassBuilder = new HashMap<>();
+        final Set<Class<?>> potentialSubstitutions = new HashSet<>();
         // Walks all cases for supplied choice in current runtime context
-        for (Class<?> caze : factory().getRuntimeContext().getCases(bindingClass())) {
+        for (final Class<?> caze : factory().getRuntimeContext().getCases(getBindingClass())) {
             // We try to load case using exact match thus name
             // and original schema must equals
-            DataContainerCodecPrototype<ChoiceCaseNode> cazeDef = loadCase(caze);
+            final DataContainerCodecPrototype<ChoiceCaseNode> cazeDef = loadCase(caze);
             // If we have case definition, this case is instantiated
             // at current location and thus is valid in context of parent choice
             if (cazeDef != null) {
                 byClassBuilder.put(cazeDef.getBindingClass(), cazeDef);
                 // Updates collection of case children
-                for (Class<? extends DataObject> cazeChild : BindingReflections.getChildrenClasses((Class) caze)) {
+                @SuppressWarnings("unchecked")
+                final Class<? extends DataObject> cazeCls = (Class<? extends DataObject>) caze;
+                for (final Class<? extends DataObject> cazeChild : BindingReflections.getChildrenClasses(cazeCls)) {
                     byCaseChildClassBuilder.put(cazeChild, cazeDef);
                 }
                 // Updates collection of YANG instance identifier to case
-                for (DataSchemaNode cazeChild : cazeDef.getSchema().getChildNodes()) {
+                for (final DataSchemaNode cazeChild : cazeDef.getSchema().getChildNodes()) {
                     byYangCaseChildBuilder.put(new NodeIdentifier(cazeChild.getQName()), cazeDef);
                 }
             } else {
@@ -68,7 +72,7 @@ final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceNode>
             }
         }
 
-        Map<Class<?>, DataContainerCodecPrototype<?>> bySubstitutionBuilder = new HashMap<>();
+        final Map<Class<?>, DataContainerCodecPrototype<?>> bySubstitutionBuilder = new HashMap<>();
         /*
          * Walks all cases which are not directly instantiated and
          * tries to match them to instantiated cases - represent same data as instantiated case,
@@ -76,8 +80,8 @@ final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceNode>
          * binding specification, that if choice is in grouping schema path location is lost,
          * and users may use incorrect case class using copy builders.
          */
-        for(Class<?> substitution : potentialSubstitutions) {
-            search: for(Entry<Class<?>, DataContainerCodecPrototype<?>> real : byClassBuilder.entrySet()) {
+        for(final Class<?> substitution : potentialSubstitutions) {
+            search: for(final Entry<Class<?>, DataContainerCodecPrototype<?>> real : byClassBuilder.entrySet()) {
                 if(BindingReflections.isSubstitutionFor(substitution, real.getKey())) {
                     bySubstitutionBuilder.put(substitution, real.getValue());
                     break search;
@@ -90,18 +94,22 @@ final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceNode>
         byCaseChildClass = ImmutableMap.copyOf(byCaseChildClassBuilder);
     }
 
+
+    @SuppressWarnings("unchecked")
     @Override
-    protected DataContainerCodecContext<?> getStreamChild(final Class<?> childClass) {
-        DataContainerCodecPrototype<?> child = byClass.get(childClass);
-        Preconditions.checkArgument(child != null,"Supplied class is not valid case",childClass);
-        return child.get();
+    public <DV extends DataObject> DataContainerCodecContext<DV, ?> streamChild(final Class<DV> childClass) {
+        final DataContainerCodecPrototype<?> child = byClass.get(childClass);
+        return (DataContainerCodecContext<DV, ?>) childNonNull(child,childClass,"Supplied class %s is not valid case").get();
     }
 
+
+    @SuppressWarnings("unchecked")
     @Override
-    protected Optional<DataContainerCodecContext<?>> getPossibleStreamChild(final Class<?> childClass) {
-        DataContainerCodecPrototype<?> child = byClass.get(childClass);
+    public <DV extends DataObject> Optional<DataContainerCodecContext<DV, ?>> possibleStreamChild(
+            final Class<DV> childClass) {
+        final DataContainerCodecPrototype<?> child = byClass.get(childClass);
         if(child != null) {
-            return Optional.<DataContainerCodecContext<?>>of(child.get());
+            return Optional.<DataContainerCodecContext<DV,?>>of((DataContainerCodecContext<DV, ?>) child.get());
         }
         return Optional.absent();
     }
@@ -111,7 +119,7 @@ final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceNode>
     }
 
     protected DataContainerCodecPrototype<ChoiceCaseNode> loadCase(final Class<?> childClass) {
-        Optional<ChoiceCaseNode> childSchema = factory().getRuntimeContext().getCaseSchemaDefinition(schema(), childClass);
+        final Optional<ChoiceCaseNode> childSchema = factory().getRuntimeContext().getCaseSchemaDefinition(schema(), childClass);
         if (childSchema.isPresent()) {
             return DataContainerCodecPrototype.from(childClass, childSchema.get(), factory());
         }
@@ -121,28 +129,50 @@ final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceNode>
     }
 
     @Override
-    protected NodeCodecContext getYangIdentifierChild(final YangInstanceIdentifier.PathArgument arg) {
-        DataContainerCodecPrototype<?> cazeProto = byYangCaseChild.get(arg);
-        Preconditions.checkArgument(cazeProto != null, "Argument %s is not valid child of %s", arg, schema());
-        return cazeProto.get().getYangIdentifierChild(arg);
+    public NodeCodecContext<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
+        final DataContainerCodecPrototype<?> cazeProto = byYangCaseChild.get(arg);
+        childNonNull(cazeProto != null, arg,"Argument %s is not valid child of %s", arg, schema());
+        return cazeProto.get().yangPathArgumentChild(arg);
     }
 
+    @SuppressWarnings("unchecked")
     @Override
-    protected Object dataFromNormalizedNode(final NormalizedNode<?, ?> data) {
-        Preconditions
-                .checkArgument(data instanceof org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode);
-        NormalizedNodeContainer<?, ?, NormalizedNode<?,?>> casted = (NormalizedNodeContainer<?, ?, NormalizedNode<?,?>>) data;
-        NormalizedNode<?, ?> first = Iterables.getFirst(casted.getValue(), null);
+    @Nullable
+    public D deserialize(final NormalizedNode<?, ?> data) {
+        Preconditions.checkArgument(data instanceof ChoiceNode);
+        final NormalizedNodeContainer<?, ?, NormalizedNode<?,?>> casted = (NormalizedNodeContainer<?, ?, NormalizedNode<?,?>>) data;
+        final NormalizedNode<?, ?> first = Iterables.getFirst(casted.getValue(), null);
 
         if (first == null) {
             return null;
         }
-        DataContainerCodecPrototype<?> caze = byYangCaseChild.get(first.getIdentifier());
-        return caze.get().dataFromNormalizedNode(data);
+        final DataContainerCodecPrototype<?> caze = byYangCaseChild.get(first.getIdentifier());
+        return (D) caze.get().deserialize(data);
+    }
+
+    DataContainerCodecContext<?, ?> getCazeByChildClass(final @Nonnull Class<? extends DataObject> type) {
+        final DataContainerCodecPrototype<?> protoCtx =
+                childNonNull(byCaseChildClass.get(type), type, "Class %s is not child of any cases for %s", type,
+                        bindingArg());
+        return protoCtx.get();
+    }
+
+    @Override
+    protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
+        return deserialize(normalizedNode);
     }
 
-    public DataContainerCodecContext<?> getCazeByChildClass(final Class<? extends DataObject> type) {
-        return byCaseChildClass.get(type).get();
+    @Override
+    public PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
+        Preconditions.checkArgument(getDomPathArgument().equals(arg));
+        return null;
+    }
+
+    @Override
+    public YangInstanceIdentifier.PathArgument serializePathArgument(
+            final PathArgument arg) {
+        // FIXME: check for null, since binding container is null.
+        return getDomPathArgument();
     }
 
-}
\ No newline at end of file
+}