BUG-865: deprecate pre-Beryllium parser elements
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / builder / impl / BuilderUtils.java
index 0eae6cace889bc54c718d9169f041f35d084bcdd..792db8d9c84b8a1a677df8511343fa097565845b 100644 (file)
@@ -12,29 +12,39 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.Iterables;
 import com.google.common.io.ByteSource;
-
-import java.io.ByteArrayOutputStream;
+import com.google.common.io.ByteStreams;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
 import java.util.Set;
 import java.util.TreeMap;
-
-import org.apache.commons.io.IOUtils;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Namespace_stmtContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 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.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -58,21 +68,28 @@ import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
+import org.opendaylight.yangtools.yang.parser.builder.api.UnknownSchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
+import org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
 import org.opendaylight.yangtools.yang.parser.util.NamedByteArrayInputStream;
 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * @deprecated Pre-Beryllium implementation, scheduled for removal.
+ */
+@Deprecated
 public final class BuilderUtils {
 
     private static final Logger LOG = LoggerFactory.getLogger(BuilderUtils.class);
-    private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings();
     private static final Splitter COLON_SPLITTER = Splitter.on(':');
     private static final Date NULL_DATE = new Date(0L);
     private static final String INPUT = "input";
     private static final String OUTPUT = "output";
+    private static final String CHILD_NOT_FOUND_IN_NODE_STR = "Child {} not found in node {}";
 
     private BuilderUtils() {
     }
@@ -109,22 +126,6 @@ public final class BuilderUtils {
         });
     }
 
-    /**
-     * Create new SchemaPath from given path and qname.
-     *
-     * @param schemaPath
-     *            base path
-     * @param qname
-     *            one or more qnames added to base path
-     * @return new SchemaPath from given path and qname
-     *
-     * @deprecated Use {@link SchemaPath#createChild(QName...)} instead.
-     */
-    @Deprecated
-    public static SchemaPath createSchemaPath(final SchemaPath schemaPath, final QName... qname) {
-        return schemaPath.createChild(qname);
-    }
-
     /**
      * Find dependent module based on given prefix
      *
@@ -138,7 +139,7 @@ public final class BuilderUtils {
      *            current line in yang model
      * @return module builder if found, null otherwise
      */
-    public static ModuleBuilder findModuleFromBuilders(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
+    public static ModuleBuilder findModuleFromBuilders(final Map<String, NavigableMap<Date, ModuleBuilder>> modules,
             final ModuleBuilder module, final String prefix, final int line) {
         ModuleBuilder dependentModule;
         Date dependentModuleRevision;
@@ -155,7 +156,7 @@ public final class BuilderUtils {
             String dependentModuleName = dependentModuleImport.getModuleName();
             dependentModuleRevision = dependentModuleImport.getRevision();
 
-            TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
+            NavigableMap<Date, ModuleBuilder> moduleBuildersByRevision = modules.get(dependentModuleName);
             if (moduleBuildersByRevision == null) {
                 return null;
             }
@@ -168,6 +169,24 @@ public final class BuilderUtils {
         return dependentModule;
     }
 
+    public static ModuleBuilder findModuleFromBuilders(final ModuleImport imp, final Iterable<ModuleBuilder> modules) {
+        String name = imp.getModuleName();
+        Date revision = imp.getRevision();
+        NavigableMap<Date, ModuleBuilder> map = new TreeMap<>();
+        for (ModuleBuilder module : modules) {
+            if (module != null && module.getName().equals(name)) {
+                map.put(module.getRevision(), module);
+            }
+        }
+        if (map.isEmpty()) {
+            return null;
+        }
+        if (revision == null) {
+            return map.lastEntry().getValue();
+        }
+        return map.get(revision);
+    }
+
     /**
      * Find module from context based on prefix.
      *
@@ -186,7 +205,7 @@ public final class BuilderUtils {
      */
     public static Module findModuleFromContext(final SchemaContext context, final ModuleBuilder currentModule,
             final String prefix, final int line) {
-        TreeMap<Date, Module> modulesByRevision = new TreeMap<>();
+        NavigableMap<Date, Module> modulesByRevision = new TreeMap<>();
 
         ModuleImport dependentModuleImport = currentModule.getImport(prefix);
         if (dependentModuleImport == null) {
@@ -218,32 +237,6 @@ public final class BuilderUtils {
         return result;
     }
 
-    /**
-     * Parse XPath string.
-     *
-     * @param xpathString
-     *            XPath as String
-     * @return SchemaPath from given String
-     */
-    public static SchemaPath parseXPathString(final String xpathString) {
-        final boolean absolute = !xpathString.isEmpty() && xpathString.charAt(0) == '/';
-
-        final List<QName> path = new ArrayList<>();
-        for (String pathElement : SLASH_SPLITTER.split(xpathString)) {
-            final Iterator<String> it = COLON_SPLITTER.split(pathElement).iterator();
-            final String s = it.next();
-
-            final QName name;
-            if (it.hasNext()) {
-                name = QName.create(QNameModule.create(null, null), s, it.next());
-            } else {
-                name = QName.create(QNameModule.create(null, null), s);
-            }
-            path.add(name);
-        }
-        return SchemaPath.create(path, absolute);
-    }
-
     /**
      * Add all augment's child nodes to given target.
      *
@@ -339,7 +332,7 @@ public final class BuilderUtils {
     /**
      * Set addedByUses flag to true for node and all its child nodes.
      *
-     * @param node
+     * @param node grouping member node
      */
     public static void setNodeAddedByUses(final GroupingMember node) {
         node.setAddedByUses(true);
@@ -356,29 +349,73 @@ public final class BuilderUtils {
         }
     }
 
-    public static DataSchemaNodeBuilder findSchemaNode(final List<QName> path, final SchemaNodeBuilder parentNode) {
-        DataSchemaNodeBuilder node = null;
+    /**
+     * Find builder of schema node under parent builder (including under
+     * AugmentationSchemaBuilder).
+     *
+     * @param path
+     *            - path of target schema node builder
+     * @param parent
+     *            - base data node container builder under which the target
+     *            schema node builder should be found
+     * @return builder of schema node
+     */
+    public static SchemaNodeBuilder findTargetNode(final Iterable<QName> path,
+            final DataNodeContainerBuilder parent) {
+
+        Preconditions.checkNotNull(parent);
+        Preconditions.checkNotNull(path);
+
+        SchemaNodeBuilder foundNode = null;
+
+        final Iterator<QName> pathIterator = path.iterator();
+        if (pathIterator.hasNext()) {
+            String name = pathIterator.next().getLocalName();
+            foundNode = parent.getDataChildByName(name);
+            if (foundNode == null) {
+                foundNode = findUnknownNode(name, parent);
+            }
+        }
+
+        if (pathIterator.hasNext() && foundNode != null) {
+            return findSchemaNode(Iterables.skip(path, 1), foundNode);
+        } else {
+            return foundNode;
+        }
+    }
+
+    public static SchemaNodeBuilder findSchemaNode(final Iterable<QName> path, final SchemaNodeBuilder parentNode) {
+        SchemaNodeBuilder node = null;
         SchemaNodeBuilder parent = parentNode;
+        int size = Iterables.size(path);
         int i = 0;
-        while (i < path.size()) {
-            String name = path.get(i).getLocalName();
+        for (QName qname : path) {
+            String name = qname.getLocalName();
             if (parent instanceof DataNodeContainerBuilder) {
                 node = ((DataNodeContainerBuilder) parent).getDataChildByName(name);
+                if (node == null) {
+                    node = findUnknownNode(name, parent);
+                }
             } else if (parent instanceof ChoiceBuilder) {
                 node = ((ChoiceBuilder) parent).getCaseNodeByName(name);
+                if (node == null) {
+                    node = findUnknownNode(name, parent);
+                }
             } else if (parent instanceof RpcDefinitionBuilder) {
-                if ("input".equals(name)) {
+                if (INPUT.equals(name)) {
                     node = ((RpcDefinitionBuilder) parent).getInput();
-                } else if ("output".equals(name)) {
+                } else if (OUTPUT.equals(name)) {
                     node = ((RpcDefinitionBuilder) parent).getOutput();
                 } else {
-                    return null;
+                    if (node == null) {
+                        node = findUnknownNode(name, parent);
+                    }
                 }
             } else {
-                return null;
+                node = findUnknownNode(name, parent);
             }
 
-            if (i < path.size() - 1) {
+            if (i < size - 1) {
                 parent = node;
             }
             i = i + 1;
@@ -387,6 +424,15 @@ public final class BuilderUtils {
         return node;
     }
 
+    private static UnknownSchemaNodeBuilder findUnknownNode(final String name, final Builder parent) {
+        for (UnknownSchemaNodeBuilder un : parent.getUnknownNodes()) {
+            if (un.getQName().getLocalName().equals(name)) {
+                return un;
+            }
+        }
+        return null;
+    }
+
     /**
      *
      * Find a builder for node in data namespace of YANG module.
@@ -413,7 +459,16 @@ public final class BuilderUtils {
         Optional<SchemaNodeBuilder> currentNode = getDataNamespaceChild(module, first);
 
         while (currentNode.isPresent() && path.hasNext()) {
-            currentNode = findDataChild(currentNode.get(), path.next());
+            SchemaNodeBuilder currentParent = currentNode.get();
+            QName currentPath = path.next();
+            currentNode = findDataChild(currentParent, currentPath);
+            if (!currentNode.isPresent()) {
+                for (SchemaNodeBuilder un : currentParent.getUnknownNodes()) {
+                    if (un.getQName().equals(currentPath)) {
+                        currentNode = Optional.of(un);
+                    }
+                }
+            }
         }
         return currentNode;
     }
@@ -426,9 +481,8 @@ public final class BuilderUtils {
             return castOptional(SchemaNodeBuilder.class, findCaseInChoice((ChoiceBuilder) parent, child));
         } else if (parent instanceof RpcDefinitionBuilder) {
             return castOptional(SchemaNodeBuilder.class, findContainerInRpc((RpcDefinitionBuilder) parent, child));
-
         } else {
-            LOG.trace("Child {} not found in node {}", child, parent);
+            LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, parent);
             return Optional.absent();
         }
     }
@@ -440,7 +494,7 @@ public final class BuilderUtils {
      *            Class to be checked
      * @param optional
      *            Original value
-     * @return
+     * @return Optional object with type argument casted as cls
      */
     private static <T> Optional<T> castOptional(final Class<T> cls, final Optional<?> optional) {
         if (optional.isPresent()) {
@@ -455,6 +509,7 @@ public final class BuilderUtils {
         return Optional.absent();
     }
 
+    // FIXME: if rpc does not define input or output, this method creates it
     /**
      *
      * Gets input / output container from {@link RpcDefinitionBuilder} if QName
@@ -468,13 +523,30 @@ public final class BuilderUtils {
      * @return Optional of input/output if defined and QName is input/output.
      *         Otherwise {@link Optional#absent()}.
      */
-    private static Optional<ContainerSchemaNodeBuilder> findContainerInRpc(final RpcDefinitionBuilder parent, final QName child) {
+    private static Optional<ContainerSchemaNodeBuilder> findContainerInRpc(final RpcDefinitionBuilder parent,
+            final QName child) {
         if (INPUT.equals(child.getLocalName())) {
+            if (parent.getInput() == null) {
+                QName qname = QName.create(parent.getQName().getModule(), INPUT);
+                final ContainerSchemaNodeBuilder inputBuilder = new ContainerSchemaNodeBuilder(parent.getModuleName(),
+                        parent.getLine(), qname, parent.getPath().createChild(qname));
+                inputBuilder.setParent(parent);
+                parent.setInput(inputBuilder);
+                return Optional.of(inputBuilder);
+            }
             return Optional.of(parent.getInput());
         } else if (OUTPUT.equals(child.getLocalName())) {
+            if (parent.getOutput() == null) {
+                QName qname = QName.create(parent.getQName().getModule(), OUTPUT);
+                final ContainerSchemaNodeBuilder outputBuilder = new ContainerSchemaNodeBuilder(parent.getModuleName(),
+                        parent.getLine(), qname, parent.getPath().createChild(qname));
+                outputBuilder.setParent(parent);
+                parent.setOutput(outputBuilder);
+                return Optional.of(outputBuilder);
+            }
             return Optional.of(parent.getOutput());
         }
-        LOG.trace("Child {} not found in node {}", child, parent);
+        LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, parent);
         return Optional.absent();
     }
 
@@ -495,7 +567,7 @@ public final class BuilderUtils {
                 return Optional.of(caze);
             }
         }
-        LOG.trace("Child {} not found in node {}", child, parent);
+        LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, parent);
         return Optional.absent();
     }
 
@@ -516,7 +588,7 @@ public final class BuilderUtils {
                 return Optional.of(childNode);
             }
         }
-        LOG.trace("Child {} not found in node {}", child, parent);
+        LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, parent);
         return Optional.absent();
     }
 
@@ -576,7 +648,7 @@ public final class BuilderUtils {
                 return Optional.<SchemaNodeBuilder> of(childNode);
             }
         }
-        LOG.trace("Child {} not found in node {}", child, builder);
+        LOG.trace(CHILD_NOT_FOUND_IN_NODE_STR, child, builder);
         return Optional.absent();
     }
 
@@ -584,18 +656,20 @@ public final class BuilderUtils {
      * Find augment target node and perform augmentation.
      *
      * @param augment
+     *            augment builder to process
      * @param firstNodeParent
      *            parent of first node in path
-     * @param path
-     *            path to augment target
      * @return true if augmentation process succeed, false otherwise
      */
     public static boolean processAugmentation(final AugmentationSchemaBuilder augment,
             final ModuleBuilder firstNodeParent) {
-        Optional<SchemaNodeBuilder> potentialTargetNode = findSchemaNodeInModule(augment.getTargetNodeSchemaPath(),
+        Optional<SchemaNodeBuilder> potentialTargetNode = findSchemaNodeInModule(augment.getTargetPath(),
                 firstNodeParent);
         if (!potentialTargetNode.isPresent()) {
             return false;
+        } else if (potentialTargetNode.get() instanceof UnknownSchemaNodeBuilder) {
+            LOG.warn("Error in augment parsing: unsupported augment target: {}", potentialTargetNode.get());
+            return true;
         }
         SchemaNodeBuilder targetNode = potentialTargetNode.get();
         fillAugmentTarget(augment, targetNode);
@@ -606,8 +680,8 @@ public final class BuilderUtils {
         return true;
     }
 
-    public static IdentitySchemaNodeBuilder findBaseIdentity(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
-            final ModuleBuilder module, final String baseString, final int line) {
+    public static IdentitySchemaNodeBuilder findBaseIdentity(final ModuleBuilder module, final String baseString,
+            final int line) {
 
         // FIXME: optimize indexOf() away?
         if (baseString.indexOf(':') != -1) {
@@ -643,7 +717,7 @@ public final class BuilderUtils {
     /**
      * Get module in which this node is defined.
      *
-     * @param node
+     * @param node node
      * @return builder of module where this node is defined
      */
     public static ModuleBuilder getParentModule(final Builder node) {
@@ -662,8 +736,8 @@ public final class BuilderUtils {
     }
 
     public static Set<DataSchemaNodeBuilder> wrapChildNodes(final String moduleName, final int line,
-            final Set<DataSchemaNode> nodes, final SchemaPath parentPath, final QName parentQName) {
-        Set<DataSchemaNodeBuilder> result = new HashSet<>();
+            final Collection<DataSchemaNode> nodes, final SchemaPath parentPath, final QName parentQName) {
+        Set<DataSchemaNodeBuilder> result = new LinkedHashSet<>(nodes.size());
 
         for (DataSchemaNode node : nodes) {
             QName qname = QName.create(parentQName, node.getQName().getLocalName());
@@ -679,19 +753,19 @@ public final class BuilderUtils {
         final SchemaPath schemaPath = parentPath.createChild(qname);
 
         if (node instanceof AnyXmlSchemaNode) {
-            return new AnyXmlBuilder(moduleName, line, qname, schemaPath, ((AnyXmlSchemaNode) node));
-        } else if (node instanceof ChoiceNode) {
-            return new ChoiceBuilder(moduleName, line, qname, schemaPath, ((ChoiceNode) node));
+            return new AnyXmlBuilder(moduleName, line, qname, schemaPath, (AnyXmlSchemaNode) node);
+        } else if (node instanceof ChoiceSchemaNode) {
+            return new ChoiceBuilder(moduleName, line, qname, schemaPath, (ChoiceSchemaNode) node);
         } else if (node instanceof ContainerSchemaNode) {
-            return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ContainerSchemaNode) node));
+            return new ContainerSchemaNodeBuilder(moduleName, line, qname, schemaPath, (ContainerSchemaNode) node);
         } else if (node instanceof LeafSchemaNode) {
-            return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafSchemaNode) node));
+            return new LeafSchemaNodeBuilder(moduleName, line, qname, schemaPath, (LeafSchemaNode) node);
         } else if (node instanceof LeafListSchemaNode) {
-            return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((LeafListSchemaNode) node));
+            return new LeafListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (LeafListSchemaNode) node);
         } else if (node instanceof ListSchemaNode) {
-            return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, ((ListSchemaNode) node));
+            return new ListSchemaNodeBuilder(moduleName, line, qname, schemaPath, (ListSchemaNode) node);
         } else if (node instanceof ChoiceCaseNode) {
-            return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, ((ChoiceCaseNode) node));
+            return new ChoiceCaseBuilder(moduleName, line, qname, schemaPath, (ChoiceCaseNode) node);
         } else {
             throw new YangParseException(moduleName, line, "Failed to copy node: Unknown type of DataSchemaNode: "
                     + node);
@@ -716,7 +790,7 @@ public final class BuilderUtils {
         for (TypeDefinition<?> node : nodes) {
             QName qname = QName.create(parentQName, node.getQName().getLocalName());
             SchemaPath schemaPath = parentPath.createChild(qname);
-            result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, ((ExtendedType) node)));
+            result.add(new TypeDefinitionBuilderImpl(moduleName, line, qname, schemaPath, (ExtendedType) node));
         }
         return result;
     }
@@ -734,16 +808,16 @@ public final class BuilderUtils {
 
     private static final class ByteSourceImpl extends ByteSource {
         private final String toString;
-        private final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        private final byte[] data;
 
         private ByteSourceImpl(final InputStream input) throws IOException {
             toString = input.toString();
-            IOUtils.copy(input, output);
+            data = ByteStreams.toByteArray(input);
         }
 
         @Override
         public InputStream openStream() throws IOException {
-            return new NamedByteArrayInputStream(output.toByteArray(), toString);
+            return new NamedByteArrayInputStream(data, toString);
         }
     }
 
@@ -755,4 +829,127 @@ public final class BuilderUtils {
         }
     }
 
+    public static ModuleBuilder findModule(final QName qname, final Map<URI, NavigableMap<Date, ModuleBuilder>> modules) {
+        NavigableMap<Date, ModuleBuilder> map = modules.get(qname.getNamespace());
+        if (map == null) {
+            return null;
+        }
+        if (qname.getRevision() == null) {
+            return map.lastEntry().getValue();
+        }
+
+        final Entry<Date, ModuleBuilder> lastEntry = map.lastEntry();
+        if (qname.getRevision().compareTo(lastEntry.getKey()) > 0) {
+            /*
+             * We are trying to find more recent revision of module than is in
+             * the map. Most probably the yang models are not referenced
+             * correctly and the revision of a base module or submodule has not
+             * been updated along with revision of a referenced module or
+             * submodule. However, we should return the most recent entry in the
+             * map, otherwise the null pointer exception occurs (see Bug3799).
+             */
+            LOG.warn(String
+                    .format("Attempt to find more recent revision of module than is available. "
+                            + "The requested revision is [%s], but the most recent available revision of module is [%s]."
+                            + " Most probably some of Yang models do not have updated revision or they are not "
+                            + "referenced correctly.",
+                            qname.getRevision(), lastEntry.getKey()));
+            return lastEntry.getValue();
+        }
+
+        return map.get(qname.getRevision());
+    }
+
+    public static Map<String, NavigableMap<Date, URI>> createYangNamespaceContext(
+            final Collection<? extends ParseTree> modules, final Optional<SchemaContext> context) {
+        Map<String, NavigableMap<Date, URI>> namespaceContext = new HashMap<>();
+        Set<Submodule_stmtContext> submodules = new HashSet<>();
+        // first read ParseTree collection and separate modules and submodules
+        for (ParseTree module : modules) {
+            for (int i = 0; i < module.getChildCount(); i++) {
+                ParseTree moduleTree = module.getChild(i);
+                if (moduleTree instanceof Submodule_stmtContext) {
+                    // put submodule context to separate collection
+                    submodules.add((Submodule_stmtContext) moduleTree);
+                } else if (moduleTree instanceof Module_stmtContext) {
+                    // get name, revision and namespace from module
+                    Module_stmtContext moduleCtx = (Module_stmtContext) moduleTree;
+                    final String moduleName = ParserListenerUtils.stringFromNode(moduleCtx);
+                    Date rev = null;
+                    URI namespace = null;
+                    for (int j = 0; j < moduleCtx.getChildCount(); j++) {
+                        ParseTree moduleCtxChildTree = moduleCtx.getChild(j);
+                        if (moduleCtxChildTree instanceof Revision_stmtsContext) {
+                            String revisionDateStr = YangModelDependencyInfo
+                                    .getLatestRevision((Revision_stmtsContext) moduleCtxChildTree);
+                            if (revisionDateStr == null) {
+                                rev = new Date(0);
+                            } else {
+                                rev = QName.parseRevision(revisionDateStr);
+                            }
+                        }
+                        if (moduleCtxChildTree instanceof Module_header_stmtsContext) {
+                            Module_header_stmtsContext headerCtx = (Module_header_stmtsContext) moduleCtxChildTree;
+                            for (int k = 0; k < headerCtx.getChildCount(); k++) {
+                                ParseTree ctx = headerCtx.getChild(k);
+                                if (ctx instanceof Namespace_stmtContext) {
+                                    final String namespaceStr = ParserListenerUtils.stringFromNode(ctx);
+                                    namespace = URI.create(namespaceStr);
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    // update namespaceContext
+                    NavigableMap<Date, URI> revToNs = namespaceContext.get(moduleName);
+                    if (revToNs == null) {
+                        revToNs = new TreeMap<>();
+                        revToNs.put(rev, namespace);
+                        namespaceContext.put(moduleName, revToNs);
+                    }
+                    revToNs.put(rev, namespace);
+                }
+            }
+        }
+        // after all ParseTree-s are parsed update namespaceContext with modules
+        // from SchemaContext
+        if (context.isPresent()) {
+            for (Module module : context.get().getModules()) {
+                NavigableMap<Date, URI> revToNs = namespaceContext.get(module.getName());
+                if (revToNs == null) {
+                    revToNs = new TreeMap<>();
+                    revToNs.put(module.getRevision(), module.getNamespace());
+                    namespaceContext.put(module.getName(), revToNs);
+                }
+                revToNs.put(module.getRevision(), module.getNamespace());
+            }
+        }
+        // when all modules are processed, traverse submodules and update
+        // namespaceContext with mapping for submodules
+        for (Submodule_stmtContext submodule : submodules) {
+            final String moduleName = ParserListenerUtils.stringFromNode(submodule);
+            for (int i = 0; i < submodule.getChildCount(); i++) {
+                ParseTree subHeaderCtx = submodule.getChild(i);
+                if (subHeaderCtx instanceof Submodule_header_stmtsContext) {
+                    for (int j = 0; j < subHeaderCtx.getChildCount(); j++) {
+                        ParseTree belongsCtx = subHeaderCtx.getChild(j);
+                        if (belongsCtx instanceof Belongs_to_stmtContext) {
+                            final String belongsTo = ParserListenerUtils.stringFromNode(belongsCtx);
+                            NavigableMap<Date, URI> ns = namespaceContext.get(belongsTo);
+                            if (ns == null) {
+                                throw new YangParseException(moduleName, submodule.getStart().getLine(), String.format(
+                                        "Unresolved belongs-to statement: %s", belongsTo));
+                            }
+                            // submodule get namespace and revision from module
+                            NavigableMap<Date, URI> subNs = new TreeMap<>();
+                            subNs.put(ns.firstKey(), ns.firstEntry().getValue());
+                            namespaceContext.put(moduleName, subNs);
+                        }
+                    }
+                }
+            }
+        }
+        return namespaceContext;
+    }
+
 }