ONOS-4971: Synthetic Link Data -- WIP

- Enhancing UiRegion to capture the hierarchical (parent/child) relationships captured in the UiTopoLayouts.

Change-Id: I152e0d52d4580b14b679f3387402077f16f61e6a
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoLayoutManager.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoLayoutManager.java
index fcf4c46..cdd95cf 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoLayoutManager.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoLayoutManager.java
@@ -24,17 +24,20 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.KryoNamespace;
+import org.onosproject.net.region.RegionId;
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.service.ConsistentMap;
 import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.StorageService;
 import org.onosproject.ui.UiTopoLayoutService;
+import org.onosproject.ui.model.topo.UiRegion;
 import org.onosproject.ui.model.topo.UiTopoLayout;
 import org.onosproject.ui.model.topo.UiTopoLayoutId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -111,6 +114,18 @@
     }
 
     @Override
+    public UiTopoLayout getLayout(RegionId regionId) {
+        if (regionId == null || regionId.equals(UiRegion.NULL_ID)) {
+            return getRootLayout();
+        }
+
+        List<UiTopoLayout> matchingLayouts = layoutMap.values().stream()
+                .filter(l -> Objects.equals(regionId, l.regionId()))
+                .collect(Collectors.toList());
+        return matchingLayouts.isEmpty() ? null : matchingLayouts.get(0);
+    }
+
+    @Override
     public Set<UiTopoLayout> getPeerLayouts(UiTopoLayoutId layoutId) {
         checkNotNull(layoutId, ID_NULL);
 
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/cli/ListRegions.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/cli/ListRegions.java
index cdb8bd5..70e0826 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/cli/ListRegions.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/cli/ListRegions.java
@@ -29,6 +29,7 @@
     @Override
     protected void execute() {
         UiSharedTopologyModel model = get(UiSharedTopologyModel.class);
+        print("%s", model.getNullRegion());
         sorted(model.getRegions()).forEach(r -> print("%s", r));
     }
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
index 4ca6b42..781650d 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
@@ -29,6 +29,7 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionId;
+import org.onosproject.ui.UiTopoLayoutService;
 import org.onosproject.ui.model.ServiceBundle;
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiDevice;
@@ -37,6 +38,8 @@
 import org.onosproject.ui.model.topo.UiLink;
 import org.onosproject.ui.model.topo.UiLinkId;
 import org.onosproject.ui.model.topo.UiRegion;
+import org.onosproject.ui.model.topo.UiTopoLayout;
+import org.onosproject.ui.model.topo.UiTopoLayoutId;
 import org.onosproject.ui.model.topo.UiTopology;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -202,6 +205,34 @@
 
         // Make sure the region object refers to the devices
         region.reconcileDevices(deviceIds);
+
+        fixupContainmentHierarchy(region);
+    }
+
+    private void fixupContainmentHierarchy(UiRegion region) {
+        UiTopoLayoutService ls = services.layout();
+        RegionId regionId = region.id();
+
+        UiTopoLayout layout = ls.getLayout(regionId);
+        if (layout == null) {
+            // no layout backed by this region
+            log.warn("No layout backed by region {}", regionId);
+            return;
+        }
+
+        UiTopoLayoutId layoutId = layout.id();
+
+        if (!layout.isRoot()) {
+            UiTopoLayoutId parentId = layout.parent();
+            UiTopoLayout parentLayout = ls.getLayout(parentId);
+            RegionId parentRegionId = parentLayout.regionId();
+            region.setParent(parentRegionId);
+        }
+
+        Set<UiTopoLayout> kids = ls.getChildren(layoutId);
+        Set<RegionId> kidRegionIds = new HashSet<>(kids.size());
+        kids.forEach(k -> kidRegionIds.add(k.regionId()));
+        region.setChildren(kidRegionIds);
     }
 
     private void loadRegions() {
@@ -478,7 +509,11 @@
     public void refresh() {
         // fix up internal linkages if they aren't correct
 
-        // at the moment, this is making sure devices are in the correct region
+        // make sure regions reflect layout containment hierarchy
+        fixupContainmentHierarchy(uiTopology.nullRegion());
+        uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
+
+        // make sure devices are in the correct region
         Set<UiDevice> allDevices = uiTopology.allDevices();
 
         services.region().getRegions().forEach(r -> {
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
index 4a5cc91..a12e34d 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
@@ -60,6 +60,7 @@
 import org.onosproject.net.region.RegionService;
 import org.onosproject.net.statistic.StatisticService;
 import org.onosproject.net.topology.TopologyService;
+import org.onosproject.ui.UiTopoLayoutService;
 import org.onosproject.ui.impl.topo.UiTopoSession;
 import org.onosproject.ui.model.ServiceBundle;
 import org.onosproject.ui.model.topo.UiClusterMember;
@@ -87,6 +88,9 @@
             LoggerFactory.getLogger(UiSharedTopologyModel.class);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private UiTopoLayoutService layoutService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private ClusterService clusterService;
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private MastershipService mastershipService;
@@ -282,6 +286,11 @@
      */
     private class DefaultServiceBundle implements ServiceBundle {
         @Override
+        public UiTopoLayoutService layout() {
+            return layoutService;
+        }
+
+        @Override
         public ClusterService cluster() {
             return clusterService;
         }