Add SchemaContext to NormalizedNode parser in RESTCONF 25/16025/5
authorVaclav Demcak <vdemcak@cisco.com>
Fri, 6 Mar 2015 13:35:45 +0000 (14:35 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Sun, 8 Mar 2015 13:29:07 +0000 (14:29 +0100)
Correct parsing of identities, instance-identifiers
and others, requires access to full SchemaContext,
not only subset of it.

Migrated to parser factory, which provides that
access in order to correctly deserialize
context-sensitive types.

Change-Id: Ic92b9ce745080076a10d654acaba39e5c59f2b42
Signed-off-by: Vaclav Demcak <vdemcak@cisco.com>
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java

index ad7122058c880d3ab270d40ae56ef6775ccd3c2f..905cb50c3a8bbcc2f916fb62cdb2ec017903b45e 100644 (file)
@@ -12,6 +12,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import javax.ws.rs.Consumes;
@@ -34,6 +35,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.slf4j.Logger;
@@ -47,18 +49,17 @@ import org.w3c.dom.Element;
 public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<NormalizedNodeContext> {
 
     private final static Logger LOG = LoggerFactory.getLogger(XmlNormalizedNodeBodyReader.class);
-    private final static DomToNormalizedNodeParserFactory DOM_PARSER_FACTORY = DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER);
     private static final DocumentBuilderFactory BUILDERFACTORY;
 
     static {
-        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         try {
             factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
             factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
             factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
             factory.setXIncludeAware(false);
             factory.setExpandEntityReferences(false);
-        } catch (ParserConfigurationException e) {
+        } catch (final ParserConfigurationException e) {
             throw new ExceptionInInitializerError(e);
         }
         factory.setNamespaceAware(true);
@@ -80,19 +81,19 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
             final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws IOException,
             WebApplicationException {
         try {
-            Optional<InstanceIdentifierContext> path = getIdentifierWithSchema();
+            final Optional<InstanceIdentifierContext> path = getIdentifierWithSchema();
 
             final DocumentBuilder dBuilder;
             try {
                 dBuilder = BUILDERFACTORY.newDocumentBuilder();
-            } catch (ParserConfigurationException e) {
+            } catch (final ParserConfigurationException e) {
                 throw new RuntimeException("Failed to parse XML document", e);
             }
-            Document doc = dBuilder.parse(entityStream);
+            final Document doc = dBuilder.parse(entityStream);
 
-            NormalizedNode<?, ?> result = parse(path.get(),doc);
+            final NormalizedNode<?, ?> result = parse(path.get(),doc);
             return new NormalizedNodeContext(path.get(),result);
-        } catch (Exception e) {
+        } catch (final Exception e) {
             LOG.debug("Error parsing json input", e);
 
             throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
@@ -100,14 +101,34 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
         }
     }
 
-    private static NormalizedNode<?,?> parse(InstanceIdentifierContext pathContext,Document doc) {
-        List<Element> elements = Collections.singletonList(doc.getDocumentElement());
+    private static NormalizedNode<?,?> parse(final InstanceIdentifierContext pathContext,final Document doc) {
+
+        final List<Element> elements = Collections.singletonList(doc.getDocumentElement());
         DataSchemaNode schemaNode = pathContext.getSchemaNode();
+
+        final String docRootElm = doc.getDocumentElement().getLocalName();
+        final String schemaNodeName = pathContext.getSchemaNode().getQName().getLocalName();
+
+        // TODO : do we want to really follow netconf-restconf specification ?
+        if (!schemaNodeName.equalsIgnoreCase(docRootElm)) {
+            final Collection<DataSchemaNode> children = ((DataNodeContainer) schemaNode).getChildNodes();
+            for (final DataSchemaNode child : children) {
+                if (child.getQName().getLocalName().equalsIgnoreCase(docRootElm)) {
+                    schemaNode = child;
+                    break;
+                }
+            }
+        }
+
+        // FIXME the factory instance should be cached if the schema context is the same
+        final DomToNormalizedNodeParserFactory parserFactory =
+                DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, pathContext.getSchemaContext());
+
         if(schemaNode instanceof ContainerSchemaNode) {
-            return DOM_PARSER_FACTORY.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode);
+            return parserFactory.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode);
         } else if(schemaNode instanceof ListSchemaNode) {
-            ListSchemaNode casted = (ListSchemaNode) schemaNode;
-            return DOM_PARSER_FACTORY.getMapEntryNodeParser().parse(elements, casted);
+            final ListSchemaNode casted = (ListSchemaNode) schemaNode;
+            return parserFactory.getMapEntryNodeParser().parse(elements, casted);
         }
         return null;
     }