BUG-7054: Check YinDomSchemaSource validity 53/47753/7
authorRobert Varga <rovarga@cisco.com>
Sat, 29 Oct 2016 22:52:21 +0000 (00:52 +0200)
committerRobert Varga <nite@hq.sk>
Fri, 4 Nov 2016 10:08:58 +0000 (10:08 +0000)
This adds defensive checks on DOMSource, so that basic
YIN structure (namespace, root element, revision, module name)
are discovered.

These are used to construct an accurate SourceIdentifier, which
is then used as the identifier.

Change-Id: I2d2245a903ce687e860d04905f0eb0a966617ceb
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinDomSchemaSource.java

index 3cd7bf6bbd0f56ad4b404ab83c65e637a06bbb8b..a6bf7cfdc4d0ef8f3e186dc8935e7b1a7fd85b84 100644 (file)
@@ -7,8 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YIN_MODULE;
+import static org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping.MODULE;
+import static org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping.REVISION;
+import static org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping.SUBMODULE;
+
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import javax.annotation.Nonnull;
 import javax.xml.transform.Source;
@@ -16,12 +22,22 @@ import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * Utility {@link YinXmlSchemaSource} exposing a W3C {@link DOMSource} representation of YIN model.
  */
 public abstract class YinDomSchemaSource implements YinXmlSchemaSource {
+    private static final Logger LOG = LoggerFactory.getLogger(YinDomSchemaSource.class);
     private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
+    private static final QName REVISION_STMT = REVISION.getStatementName();
 
     YinDomSchemaSource() {
         // Prevent outside instantiation
@@ -34,28 +50,50 @@ public abstract class YinDomSchemaSource implements YinXmlSchemaSource {
      * @param source W3C DOM source
      * @return A new {@link YinDomSchemaSource} instance.
      */
-    @Nonnull public static YinDomSchemaSource create(@Nonnull final SourceIdentifier identifier, @Nonnull final DOMSource source) {
-        return new Simple(identifier, source);
-    }
-
-    private static YinDomSchemaSource castSchemaSource(final YinXmlSchemaSource xmlSchemaSource) {
-        if (xmlSchemaSource instanceof YinDomSchemaSource) {
-            return (YinDomSchemaSource) xmlSchemaSource;
+    public static @Nonnull YinDomSchemaSource create(@Nonnull final SourceIdentifier identifier,
+            final @Nonnull DOMSource source) {
+
+        final Node root = source.getNode().getFirstChild();
+        final String rootNs = root.getNamespaceURI();
+        if (rootNs == null) {
+            // Let whoever is using this deal with this
+            return new Simple(identifier, source);
         }
 
-        final Source source = xmlSchemaSource.getSource();
-        if (source instanceof DOMSource) {
-            return create(xmlSchemaSource.getIdentifier(), (DOMSource) source);
-        }
+        final QName qname = QName.create(rootNs, root.getLocalName());
+        Preconditions.checkArgument(RFC6020_YIN_MODULE.equals(qname.getModule()),
+            "Root node namepsace %s does not match %s", rootNs, YangConstants.RFC6020_YIN_NAMESPACE);
+        Preconditions.checkArgument(MODULE.getStatementName().equals(qname) ||
+            SUBMODULE.getStatementName().equals(qname), "Root element %s is not a module nor a submodule", qname);
 
-        return null;
-    }
+        Preconditions.checkArgument(root instanceof Element, "Root node %s is not an element", root);
+        final Element element = (Element)root;
 
-    static DOMSource transformSource(final Source source) throws TransformerException {
-        final DOMResult result = new DOMResult();
-        TRANSFORMER_FACTORY.newTransformer().transform(source, result);
+        final Attr nameAttr = element.getAttributeNode(MODULE.getArgumentName().getLocalName());
+        Preconditions.checkArgument(nameAttr != null, "No %s name argument found in %s", element.getLocalName());
 
-        return new DOMSource(result.getNode(), result.getSystemId());
+        final NodeList revisions = element.getElementsByTagNameNS(REVISION_STMT.getNamespace().toString(),
+            REVISION_STMT.getLocalName());
+        if (revisions.getLength() == 0) {
+            // FIXME: is module name important (as that may have changed)
+            return new Simple(identifier, source);
+        }
+
+        final Element revisionStmt = (Element) revisions.item(0);
+        final Attr dateAttr = revisionStmt.getAttributeNode(REVISION.getArgumentName().getLocalName());
+        Preconditions.checkArgument(dateAttr != null, "No revision statement argument found in %s", revisionStmt);
+
+        final SourceIdentifier parsedId = RevisionSourceIdentifier.create(nameAttr.getValue(),
+            Optional.of(dateAttr.getValue()));
+        final SourceIdentifier id;
+        if (!parsedId.equals(identifier)) {
+            LOG.debug("Changed identifier from {} to {}", identifier, parsedId);
+            id = parsedId;
+        } else {
+            id = identifier;
+        }
+
+        return new Simple(id, source);
     }
 
     /**
@@ -85,14 +123,14 @@ public abstract class YinDomSchemaSource implements YinXmlSchemaSource {
             create(xmlSchemaSource.getIdentifier(), transformSource(xmlSchemaSource.getSource()));
     }
 
+    @Override
+    @Nonnull public abstract DOMSource getSource();
+
     @Override
     public final Class<? extends YinXmlSchemaSource> getType() {
         return YinDomSchemaSource.class;
     }
 
-    @Override
-    @Nonnull public abstract DOMSource getSource();
-
     @Override
     public final String toString() {
         return addToStringAttributes(MoreObjects.toStringHelper(this).add("identifier", getIdentifier())).toString();
@@ -109,6 +147,26 @@ public abstract class YinDomSchemaSource implements YinXmlSchemaSource {
      */
     protected abstract ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper);
 
+    static DOMSource transformSource(final Source source) throws TransformerException {
+        final DOMResult result = new DOMResult();
+        TRANSFORMER_FACTORY.newTransformer().transform(source, result);
+
+        return new DOMSource(result.getNode(), result.getSystemId());
+    }
+
+    private static YinDomSchemaSource castSchemaSource(final YinXmlSchemaSource xmlSchemaSource) {
+        if (xmlSchemaSource instanceof YinDomSchemaSource) {
+            return (YinDomSchemaSource) xmlSchemaSource;
+        }
+
+        final Source source = xmlSchemaSource.getSource();
+        if (source instanceof DOMSource) {
+            return create(xmlSchemaSource.getIdentifier(), (DOMSource) source);
+        }
+
+        return null;
+    }
+
     private static final class Simple extends YinDomSchemaSource {
         private final SourceIdentifier identifier;
         private final DOMSource source;