Bug 4969: NPE in JSONCodecFactory by attempt to find codec for a leafref
[yangtools.git] / yang / yang-data-codec-gson / src / main / java / org / opendaylight / yangtools / yang / data / codec / gson / JSONCodecFactory.java
index c99d46404885c0b6c9b9e04dc24a64e0dbe0106f..1d6ffc827724a1670da028bdc5a34cc9810ccc43 100644 (file)
@@ -9,17 +9,24 @@ package org.opendaylight.yangtools.yang.data.codec.gson;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.gson.stream.JsonWriter;
 import java.io.IOException;
 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
-import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
-import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
+import org.opendaylight.yangtools.yang.model.util.DerivedType;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -30,7 +37,6 @@ import org.slf4j.LoggerFactory;
 @Beta
 public final class JSONCodecFactory {
     private static final Logger LOG = LoggerFactory.getLogger(JSONCodecFactory.class);
-    private static final JSONCodec<Object> LEAFREF_DEFAULT_CODEC = new JSONLeafrefCodec();
     private static final JSONCodec<Object> NULL_CODEC = new JSONCodec<Object>() {
         @Override
         public Object deserialize(final String input) {
@@ -48,55 +54,34 @@ public final class JSONCodecFactory {
         }
 
         @Override
-        public void serializeToWriter(JsonWriter writer, Object value) throws IOException {
+        public void serializeToWriter(final JsonWriter writer, final Object value) throws IOException {
             // NOOP since codec is unkwown.
             LOG.warn("Call of the serializeToWriter method on JSONCodecFactory.NULL_CODEC object. No operation performed.");
         }
     };
 
-    private static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
-        TypeDefinition<?> superType = type;
-        while (superType.getBaseType() != null) {
-            superType = superType.getBaseType();
-        }
-        return superType;
-    }
-
-    private final LoadingCache<TypeDefinition<?>, JSONCodec<Object>> codecs =
-            CacheBuilder.newBuilder().softValues().build(new CacheLoader<TypeDefinition<?>, JSONCodec<Object>>() {
-        @SuppressWarnings("unchecked")
+    private final LoadingCache<DataSchemaNode, JSONCodec<Object>> codecs =
+            CacheBuilder.newBuilder().softValues().build(new CacheLoader<DataSchemaNode, JSONCodec<Object>>() {
         @Override
-        public JSONCodec<Object> load(final TypeDefinition<?> key) throws Exception {
-            final TypeDefinition<?> type = resolveBaseTypeFrom(key);
-
-            if (type instanceof InstanceIdentifierType) {
-                return (JSONCodec<Object>) iidCodec;
-            }
-            if (type instanceof IdentityrefType) {
-                return (JSONCodec<Object>) idrefCodec;
-            }
-            if (type instanceof LeafrefTypeDefinition) {
-                return LEAFREF_DEFAULT_CODEC;
+        public JSONCodec<Object> load(final DataSchemaNode key) throws Exception {
+            final TypeDefinition<?> type;
+            if (key instanceof LeafSchemaNode) {
+                type = ((LeafSchemaNode) key).getType();
+            } else if (key instanceof LeafListSchemaNode) {
+                type = ((LeafListSchemaNode) key).getType();
+            } else {
+                throw new IllegalArgumentException("Not supported node type " + key.getClass().getName());
             }
-
-            final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codec = TypeDefinitionAwareCodec.from(type);
-            if (codec == null) {
-                LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName().getLocalName());
-                return NULL_CODEC;
-            }
-
-            return (JSONCodec<Object>) AbstractJSONCodec.create(codec);
+            return createCodec(key,type);
         }
     });
 
     private final SchemaContext schemaContext;
     private final JSONCodec<?> iidCodec;
-    private final JSONCodec<?> idrefCodec;
 
     private JSONCodecFactory(final SchemaContext context) {
         this.schemaContext = Preconditions.checkNotNull(context);
-        iidCodec = new JSONStringInstanceIdentifierCodec(context);
-        idrefCodec = new JSONStringIdentityrefCodec(context);
+        iidCodec = new JSONStringInstanceIdentifierCodec(context, this);
     }
 
     /**
@@ -109,11 +94,52 @@ public final class JSONCodecFactory {
         return new JSONCodecFactory(context);
     }
 
+    @SuppressWarnings("unchecked")
+    private JSONCodec<Object> createCodec(final DataSchemaNode key, final TypeDefinition<?> type) {
+        final TypeDefinition<?> normalizedType = DerivedType.from(type);
+        if (normalizedType instanceof LeafrefTypeDefinition) {
+            return createReferencedTypeCodec(key, (LeafrefTypeDefinition) normalizedType);
+        } else if (normalizedType instanceof IdentityrefTypeDefinition) {
+            final JSONCodec<?> jsonStringIdentityrefCodec =
+                    new JSONStringIdentityrefCodec(schemaContext, key.getQName().getModule());
+            return (JSONCodec<Object>) jsonStringIdentityrefCodec;
+        }
+        return createFromSimpleType(normalizedType);
+    }
+
+    private JSONCodec<Object> createReferencedTypeCodec(final DataSchemaNode schema,
+            final LeafrefTypeDefinition type) {
+        // FIXME: Verify if this does indeed support leafref of leafref
+        final TypeDefinition<?> referencedType =
+                SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
+        Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
+        return createCodec(schema, referencedType);
+    }
+
+    @SuppressWarnings("unchecked")
+    private JSONCodec<Object> createFromSimpleType(final TypeDefinition<?> type) {
+        if (type instanceof InstanceIdentifierTypeDefinition) {
+            return (JSONCodec<Object>) iidCodec;
+        }
+        if (type instanceof EmptyTypeDefinition) {
+            return JSONEmptyCodec.INSTANCE;
+        }
+
+        final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
+        if (codec == null) {
+            LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName()
+                    .getLocalName());
+            return NULL_CODEC;
+        }
+        return (JSONCodec<Object>) AbstractJSONCodec.create(codec);
+    }
+
     SchemaContext getSchemaContext() {
         return schemaContext;
     }
 
-    JSONCodec<Object> codecFor(final TypeDefinition<?> typeDefinition) {
-        return codecs.getUnchecked(typeDefinition);
+    JSONCodec<Object> codecFor(final DataSchemaNode schema) {
+        return codecs.getUnchecked(schema);
     }
+
 }