Bug 7207 - Bad parsing of identityref in typedef for last ietf-restconf
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / restconf / parser / builder / YangInstanceIdentifierDeserializer.java
index 00c3a2cd822e587233882dd83b690c5c10a8f2ee..13f4c4d208a8f6291abc91950046bc4099e98e87 100644 (file)
@@ -15,20 +15,32 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
+import org.opendaylight.netconf.sal.rest.impl.RestUtil;
+import org.opendaylight.netconf.sal.restconf.impl.RestCodec;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants;
 import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
+import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 
 /**
  * Deserializer for {@link String} to {@link YangInstanceIdentifier} for
@@ -63,9 +75,13 @@ public final class YangInstanceIdentifierDeserializer {
 
             // this is the last identifier (input is consumed) or end of identifier (slash)
             if (allCharsConsumed(variables)
-                    || currentChar(variables.getOffset(), variables.getData()) == RestconfConstants.SLASH) {
+                    || (currentChar(variables.getOffset(), variables.getData()) == RestconfConstants.SLASH)) {
                 prepareIdentifier(qname, path, variables);
-                path.add(variables.getCurrent().getIdentifier());
+                if (variables.getCurrent() == null) {
+                    path.add(NodeIdentifier.create(qname));
+                } else {
+                    path.add(variables.getCurrent().getIdentifier());
+                }
             } else if (currentChar(variables.getOffset(),
                     variables.getData()) == ParserBuilderConstants.Deserializer.EQUAL) {
                 if (nextContextNode(qname, path, variables).getDataSchemaNode() instanceof ListSchemaNode) {
@@ -96,8 +112,8 @@ public final class YangInstanceIdentifierDeserializer {
         skipCurrentChar(variables);
 
         // read key value separated by comma
-        while (keys.hasNext() && !allCharsConsumed(variables) && currentChar(variables.getOffset(),
-                variables.getData()) != RestconfConstants.SLASH) {
+        while (keys.hasNext() && !allCharsConsumed(variables) && (currentChar(variables.getOffset(),
+                variables.getData()) != RestconfConstants.SLASH)) {
 
             // empty key value
             if (currentChar(variables.getOffset(), variables.getData()) == ParserBuilderConstants.Deserializer.COMMA) {
@@ -116,12 +132,22 @@ public final class YangInstanceIdentifierDeserializer {
             );
 
             // parse value
-            values.put(keys.next(), findAndParsePercentEncoded(nextIdentifierFromNextSequence(
-                    ParserBuilderConstants.Deserializer.IDENTIFIER_PREDICATE, variables)));
+            final QName key = keys.next();
+            DataSchemaNode leafSchemaNode = null;
+            if (dataSchemaNode instanceof ListSchemaNode) {
+                leafSchemaNode = ((ListSchemaNode) dataSchemaNode).getDataChildByName(key);
+            } else if (dataSchemaNode instanceof LeafListSchemaNode) {
+                leafSchemaNode = dataSchemaNode;
+            }
+            final String value = findAndParsePercentEncoded(nextIdentifierFromNextSequence(
+                    ParserBuilderConstants.Deserializer.IDENTIFIER_PREDICATE, variables));
+            final Object valueByType = prepareValueByType(leafSchemaNode, value, variables);
+            values.put(key, valueByType);
+
 
             // skip comma
-            if (keys.hasNext() && !allCharsConsumed(variables) && currentChar(
-                    variables.getOffset(), variables.getData()) == ParserBuilderConstants.Deserializer.COMMA) {
+            if (keys.hasNext() && !allCharsConsumed(variables) && (currentChar(
+                    variables.getOffset(), variables.getData()) == ParserBuilderConstants.Deserializer.COMMA)) {
                 skipCurrentChar(variables);
             }
         }
@@ -129,7 +155,7 @@ public final class YangInstanceIdentifierDeserializer {
         // the last key is considered to be empty
         if (keys.hasNext()) {
             if (allCharsConsumed(variables)
-                    || currentChar(variables.getOffset(), variables.getData()) == RestconfConstants.SLASH) {
+                    || (currentChar(variables.getOffset(), variables.getData()) == RestconfConstants.SLASH)) {
                 values.put(keys.next(), ParserBuilderConstants.Deserializer.EMPTY_STRING);
             }
 
@@ -145,6 +171,70 @@ public final class YangInstanceIdentifierDeserializer {
         path.add(new YangInstanceIdentifier.NodeIdentifierWithPredicates(qname, values.build()));
     }
 
+    private static Object prepareValueByType(final DataSchemaNode schemaNode, final String value,
+            final MainVarsWrapper vars) {
+        Object decoded = null;
+
+        TypeDefinition<? extends TypeDefinition<?>> typedef = null;
+        if (schemaNode instanceof LeafListSchemaNode) {
+            typedef = ((LeafListSchemaNode) schemaNode).getType();
+        } else {
+            typedef = ((LeafSchemaNode) schemaNode).getType();
+        }
+        final TypeDefinition<?> baseType = RestUtil.resolveBaseTypeFrom(typedef);
+        if (baseType instanceof LeafrefTypeDefinition) {
+            typedef = SchemaContextUtil.getBaseTypeForLeafRef((LeafrefTypeDefinition) baseType, vars.getSchemaContext(),
+                    schemaNode);
+        }
+        final Codec<Object, Object> codec = RestCodec.from(typedef, null);
+        decoded = codec.deserialize(value);
+        if (decoded == null) {
+            if ((baseType instanceof IdentityrefTypeDefinition)) {
+                decoded = toQName(value, schemaNode, vars.getSchemaContext());
+            }
+        }
+        return decoded;
+    }
+
+    private static Object toQName(final String value, final DataSchemaNode schemaNode,
+            final SchemaContext schemaContext) {
+        final String moduleName = toModuleName(value);
+        final String nodeName = toNodeName(value);
+        final Module module = schemaContext.findModuleByName(moduleName, null);
+        for (final IdentitySchemaNode identitySchemaNode : module.getIdentities()) {
+            final QName qName = identitySchemaNode.getQName();
+            if (qName.getLocalName().equals(nodeName)) {
+                return qName;
+            }
+        }
+        return QName.create(schemaNode.getQName().getNamespace(), schemaNode.getQName().getRevision(), nodeName);
+    }
+
+    private static String toNodeName(final String str) {
+        final int idx = str.indexOf(':');
+        if (idx == -1) {
+            return str;
+        }
+
+        if (str.indexOf(':', idx + 1) != -1) {
+            return str;
+        }
+
+        return str.substring(idx + 1);
+    }
+
+    private static String toModuleName(final String str) {
+        final int idx = str.indexOf(':');
+        if (idx == -1) {
+            return null;
+        }
+
+        if (str.indexOf(':', idx + 1) != -1) {
+            return null;
+        }
+
+        return str.substring(0, idx);
+    }
 
     private static QName prepareQName(final MainVarsWrapper variables) {
         checkValid(
@@ -173,8 +263,8 @@ public final class YangInstanceIdentifierDeserializer {
                         variables.getOffset());
                 localName = nextIdentifierFromNextSequence(ParserBuilderConstants.Deserializer.IDENTIFIER, variables);
 
-                if (!allCharsConsumed(variables) && currentChar
-                        (variables.getOffset(), variables.getData()) == ParserBuilderConstants.Deserializer.EQUAL) {
+                if (!allCharsConsumed(variables) && (currentChar
+                        (variables.getOffset(), variables.getData()) == ParserBuilderConstants.Deserializer.EQUAL)) {
                     return getQNameOfDataSchemaNode(localName, variables);
                 } else {
                     final Module module = moduleForPrefix(prefix, variables.getSchemaContext());
@@ -215,13 +305,17 @@ public final class YangInstanceIdentifierDeserializer {
                 RestconfError.ErrorTag.MISSING_ATTRIBUTE,
                 "Value missing for: " + qname
         );
-
-        path.add(new YangInstanceIdentifier.NodeWithValue<>(qname, findAndParsePercentEncoded(value)));
+        final DataSchemaNode dataSchemaNode = variables.getCurrent().getDataSchemaNode();
+        final Object valueByType = prepareValueByType(dataSchemaNode, findAndParsePercentEncoded(value), variables);
+        path.add(new YangInstanceIdentifier.NodeWithValue<>(qname, valueByType));
     }
 
     private static void prepareIdentifier(final QName qname, final List<PathArgument> path,
             final MainVarsWrapper variables) {
         final DataSchemaContextNode<?> currentNode = nextContextNode(qname, path, variables);
+        if (currentNode == null) {
+            return;
+        }
         checkValid(!currentNode.isKeyedEntry(), "Entry " + qname + " requires key or value predicate to be present",
                 variables.getData(), variables.getOffset());
     }
@@ -230,6 +324,14 @@ public final class YangInstanceIdentifierDeserializer {
             final MainVarsWrapper variables) {
         variables.setCurrent(variables.getCurrent().getChild(qname));
         DataSchemaContextNode<?> current = variables.getCurrent();
+        if (current == null) {
+            for (final RpcDefinition rpcDefinition : variables.getSchemaContext()
+                    .findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision()).getRpcs()) {
+                if (rpcDefinition.getQName().getLocalName().equals(qname.getLocalName())) {
+                    return null;
+                }
+            }
+        }
         checkValid(current != null, qname + " is not correct schema node identifier.", variables.getData(),
                 variables.getOffset());
         while (current.isMixin()) {
@@ -320,14 +422,15 @@ public final class YangInstanceIdentifierDeserializer {
 
         private final SchemaContext schemaContext;
         private final String data;
+
         private DataSchemaContextNode<?> current;
         private int offset;
 
         public MainVarsWrapper(final String data, final DataSchemaContextNode<?> current, final int offset,
                 final SchemaContext schemaContext) {
             this.data = data;
-            this.setCurrent(current);
-            this.setOffset(offset);
+            this.current = current;
+            this.offset = offset;
             this.schemaContext = schemaContext;
         }