Merge "Bug 1446: Changed QNM to toString listener for debug output"
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / builder / impl / BuilderUtils.java
index 8d86dfd3c48bdd68ef2cd855a37d28f9836a2c4b..759e779eed26e29a079b7e37681515022e8a160a 100644 (file)
@@ -20,8 +20,6 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
@@ -35,10 +33,13 @@ import java.util.Set;
 import java.util.TreeMap;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.apache.commons.io.IOUtils;
+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.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
@@ -66,6 +67,7 @@ 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;
@@ -77,9 +79,7 @@ import org.slf4j.LoggerFactory;
 
 public final class BuilderUtils {
 
-    private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
     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";
@@ -361,8 +361,8 @@ public final class BuilderUtils {
         }
     }
 
-    public static DataSchemaNodeBuilder findSchemaNode(final Iterable<QName> path, final SchemaNodeBuilder parentNode) {
-        DataSchemaNodeBuilder node = null;
+    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;
@@ -370,18 +370,26 @@ public final class BuilderUtils {
             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)) {
                     node = ((RpcDefinitionBuilder) parent).getInput();
                 } 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 < size - 1) {
@@ -393,6 +401,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.
@@ -419,7 +436,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;
     }
@@ -446,7 +472,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()) {
@@ -461,6 +487,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
@@ -474,10 +501,27 @@ 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);
@@ -590,10 +634,9 @@ 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,
@@ -602,6 +645,9 @@ public final class BuilderUtils {
                 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);
@@ -774,11 +820,17 @@ public final class BuilderUtils {
 
     public static Map<String, TreeMap<Date, URI>> createYangNamespaceContext(
             final Collection<? extends ParseTree> modules, final Optional<SchemaContext> context) {
-        Map<String, TreeMap<Date, URI>> map = new HashMap<>();
+        Map<String, TreeMap<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 Module_stmtContext) {
+                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;
@@ -806,28 +858,56 @@ public final class BuilderUtils {
                             }
                         }
                     }
-                    TreeMap<Date, URI> revToNs = map.get(moduleName);
+                    // update namespaceContext
+                    TreeMap<Date, URI> revToNs = namespaceContext.get(moduleName);
                     if (revToNs == null) {
                         revToNs = new TreeMap<>();
                         revToNs.put(rev, namespace);
-                        map.put(moduleName, revToNs);
+                        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()) {
-                TreeMap<Date, URI> revToNs = map.get(module.getName());
+                TreeMap<Date, URI> revToNs = namespaceContext.get(module.getName());
                 if (revToNs == null) {
                     revToNs = new TreeMap<>();
                     revToNs.put(module.getRevision(), module.getNamespace());
-                    map.put(module.getName(), revToNs);
+                    namespaceContext.put(module.getName(), revToNs);
                 }
                 revToNs.put(module.getRevision(), module.getNamespace());
             }
         }
-        return map;
+        // 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);
+                            TreeMap<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
+                            TreeMap<Date, URI> subNs = new TreeMap<>();
+                            subNs.put(ns.firstKey(), ns.firstEntry().getValue());
+                            namespaceContext.put(moduleName, subNs);
+                        }
+                    }
+                }
+            }
+        }
+        return namespaceContext;
     }
 
 }