STC fixes to correctly support namespaces.

Change-Id: I196b3bc3878181bd7b3711bf8b7304f772e476db
diff --git a/utils/stc/src/main/java/org/onlab/stc/Compiler.java b/utils/stc/src/main/java/org/onlab/stc/Compiler.java
index 4d68962..6464909 100644
--- a/utils/stc/src/main/java/org/onlab/stc/Compiler.java
+++ b/utils/stc/src/main/java/org/onlab/stc/Compiler.java
@@ -31,6 +31,8 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onlab.stc.Scenario.loadScenario;
 
 /**
@@ -67,6 +69,7 @@
 
     private final Map<String, Step> steps = Maps.newHashMap();
     private final Map<String, Step> inactiveSteps = Maps.newHashMap();
+    private final Map<String, String> requirements = Maps.newHashMap();
     private final Set<Dependency> dependencies = Sets.newHashSet();
     private final List<Integer> parallels = Lists.newArrayList();
 
@@ -100,6 +103,7 @@
      */
     public void compile() {
         compile(scenario.definition(), null, null);
+        compileRequirements();
 
         // Produce the process flow
         processFlow = new ProcessFlow(ImmutableSet.copyOf(steps.values()),
@@ -150,6 +154,7 @@
                          String namespace, Group parentGroup) {
         String opfx = pfx;
         pfx = pfx + ">";
+        print("pfx=%s namespace=%s", pfx, namespace);
 
         // Scan all imports
         cfg.configurationsAt(IMPORT)
@@ -175,6 +180,26 @@
     }
 
     /**
+     * Compiles requirements for all steps and groups accrued during the
+     * overall compilation process.
+     */
+    private void compileRequirements() {
+        requirements.forEach((name, requires) ->
+                                     compileRequirements(getStep(name), requires));
+    }
+
+    private void compileRequirements(Step src, String requires) {
+        split(requires).forEach(n -> {
+            boolean isSoft = n.startsWith("~");
+            String name = n.replaceFirst("^~", "");
+            Step dst = getStep(name);
+            if (dst != null) {
+                dependencies.add(new Dependency(src, dst, isSoft));
+            }
+        });
+    }
+
+    /**
      * Processes an import directive.
      *
      * @param cfg         hierarchical definition
@@ -189,7 +214,7 @@
         print("import file=%s namespace=%s", file, newNamespace);
         try {
             Scenario importScenario = loadScenario(new FileInputStream(file));
-            compile(importScenario.definition(), namespace, parentGroup);
+            compile(importScenario.definition(), newNamespace, parentGroup);
         } catch (IOException e) {
             throw new IllegalArgumentException("Unable to import scenario", e);
         }
@@ -246,6 +271,7 @@
      */
     private boolean registerStep(Step step, HierarchicalConfiguration cfg,
                                  String namespace, Group parentGroup) {
+        checkState(!steps.containsKey(step.name()), "Step %s already exists", step.name());
         String ifClause = expand(cfg.getString(IF));
         String unlessClause = expand(cfg.getString(UNLESS));
 
@@ -259,6 +285,7 @@
         if (parentGroup != null) {
             parentGroup.addChild(step);
         }
+
         steps.put(step.name(), step);
         processRequirements(step, expand(cfg.getString(REQUIRES)), namespace);
         previous = step.name();
@@ -323,15 +350,15 @@
      * @param namespace optional namespace
      */
     private void processRequirements(Step src, String requires, String namespace) {
-        split(requires).forEach(n -> {
+        String reqs = requirements.get(src.name());
+        for (String n : split(requires)) {
             boolean isSoft = n.startsWith("~");
             String name = n.replaceFirst("^~", "");
             name = previous != null && name.equals("^") ? previous : name;
-            Step dst = getStep(name, namespace);
-            if (!inactiveSteps.containsValue(dst)) {
-                dependencies.add(new Dependency(src, dst, isSoft));
-            }
-        });
+            name = (isSoft ? "~" : "") + expand(prefix(name, namespace));
+            reqs = reqs == null ? name : (reqs + "," + name);
+        }
+        requirements.put(src.name(), reqs);
     }
 
     /**
@@ -357,7 +384,7 @@
      * @return composite name
      */
     private String prefix(String name, String namespace) {
-        return namespace != null ? namespace + "." + name : name;
+        return isNullOrEmpty(namespace) ? name : namespace + "." + name;
     }
 
     /**
diff --git a/utils/stc/src/test/java/org/onlab/stc/CompilerTest.java b/utils/stc/src/test/java/org/onlab/stc/CompilerTest.java
index bf183f3..4604cf7 100644
--- a/utils/stc/src/test/java/org/onlab/stc/CompilerTest.java
+++ b/utils/stc/src/test/java/org/onlab/stc/CompilerTest.java
@@ -70,7 +70,7 @@
 
         assertSame("incorrect scenario", scenario, compiler.scenario());
         assertEquals("incorrect step count", 24, flow.getVertexes().size());
-        assertEquals("incorrect dependency count", 17, flow.getEdges().size());
+        assertEquals("incorrect dependency count", 16, flow.getEdges().size());
         assertEquals("incorrect logDir",
                      new File(TEST_DIR.getAbsolutePath(), "foo"), compiler.logDir());