Implementing region hosts for topology 2

Change-Id: I6d1e45b1152b2387d4ff981dc0666868235eb1c3
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
index e895214..9e4beb6 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -387,7 +387,8 @@
                 .put("id", region.idAsString())
                 .put("name", region.name())
                 .put("nodeType", REGION)
-                .put("nDevs", region.deviceCount());
+                .put("nDevs", region.deviceCount())
+                .put("nHosts", region.hostCount());
         // TODO: complete closed-region details
 
         addMetaUi(node, region.idAsString());
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 30c1b80..684be94 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
@@ -193,6 +193,7 @@
     private void updateRegion(UiRegion region) {
         RegionId rid = region.id();
         Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
+        Set<HostId> hostIds = services.region().getRegionHosts(rid);
 
         // Make sure device objects refer to their region
         deviceIds.forEach(d -> {
@@ -205,8 +206,19 @@
             }
         });
 
+        hostIds.forEach(d -> {
+            UiHost host = uiTopology.findHost(d);
+            if (host != null) {
+                host.setRegionId(rid);
+            } else {
+                // if we don't have the UiDevice in the topology, what can we do?
+                log.warn("Region host {}, but we don't have UiHost in topology", d);
+            }
+        });
+
         // Make sure the region object refers to the devices
         region.reconcileDevices(deviceIds);
+        region.reconcileHosts(hostIds);
 
         fixupContainmentHierarchy(region);
     }
@@ -526,26 +538,17 @@
         fixupContainmentHierarchy(uiTopology.nullRegion());
         uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
 
-        // make sure devices are in the correct region
+        // make sure devices and hosts are in the correct region
         Set<UiDevice> allDevices = uiTopology.allDevices();
+        Set<UiHost> allHosts = uiTopology.allHosts();
 
         services.region().getRegions().forEach(r -> {
             RegionId rid = r.id();
             UiRegion region = uiTopology.findRegion(rid);
             if (region != null) {
-                Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
-                region.reconcileDevices(deviceIds);
+                reconcileDevicesWithRegion(allDevices, r, rid, region);
+                reconcileHostsWithRegion(allHosts, r, rid, region);
 
-                deviceIds.forEach(devId -> {
-                    UiDevice dev = uiTopology.findDevice(devId);
-                    if (dev != null) {
-                        dev.setRegionId(r.id());
-                        allDevices.remove(dev);
-                    } else {
-                        log.warn("Region device ID {} but no UiDevice in topology",
-                                devId);
-                    }
-                });
             } else {
                 log.warn("No UiRegion in topology for ID {}", rid);
             }
@@ -556,11 +559,49 @@
         allDevices.forEach(d -> leftOver.add(d.id()));
         uiTopology.nullRegion().reconcileDevices(leftOver);
 
+        Set<HostId> leftOverHosts = new HashSet<>(allHosts.size());
+        allHosts.forEach(h -> leftOverHosts.add(h.id()));
+        uiTopology.nullRegion().reconcileHosts(leftOverHosts);
+
         // now that we have correct region hierarchy, and devices are in their
         //  respective regions, we can compute synthetic links for each region.
         uiTopology.computeSynthLinks();
     }
 
+    private void reconcileHostsWithRegion(Set<UiHost> allHosts, Region r,
+                                          RegionId rid, UiRegion region) {
+        Set<HostId> hostIds = services.region().getRegionHosts(rid);
+        region.reconcileHosts(hostIds);
+
+        hostIds.forEach(hId -> {
+            UiHost h = uiTopology.findHost(hId);
+            if (h != null) {
+                h.setRegionId(r.id());
+                allHosts.remove(h);
+            } else {
+                log.warn("Region host ID {} but no UiHost in topology",
+                        hId);
+            }
+        });
+    }
+
+    private void reconcileDevicesWithRegion(Set<UiDevice> allDevices, Region r,
+                                            RegionId rid, UiRegion region) {
+        Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
+        region.reconcileDevices(deviceIds);
+
+        deviceIds.forEach(devId -> {
+            UiDevice dev = uiTopology.findDevice(devId);
+            if (dev != null) {
+                dev.setRegionId(r.id());
+                allDevices.remove(dev);
+            } else {
+                log.warn("Region device ID {} but no UiDevice in topology",
+                        devId);
+            }
+        });
+    }
+
 
     // === CACHE STATISTICS
 
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
index de7ec9a..ea48bb1 100644
--- a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
@@ -52,7 +52,6 @@
 import org.onosproject.net.region.DefaultRegion;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionId;
-import org.onosproject.net.region.RegionListener;
 import org.onosproject.net.region.RegionService;
 import org.onosproject.ui.UiTopoLayoutService;
 import org.onosproject.ui.impl.AbstractUiImplTest;
@@ -501,8 +500,7 @@
         }
     }
 
-    // TODO: consider implementing RegionServiceAdapter and extending that here
-    private static class MockRegionService implements RegionService {
+    private static class MockRegionService extends RegionServiceAdapter {
 
         private final Map<RegionId, Region> lookup = new HashMap<>();
 
@@ -549,14 +547,6 @@
             }
             return Collections.emptySet();
         }
-
-        @Override
-        public void addListener(RegionListener listener) {
-        }
-
-        @Override
-        public void removeListener(RegionListener listener) {
-        }
     }
 
 
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/RegionServiceAdapter.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/RegionServiceAdapter.java
new file mode 100644
index 0000000..4f7ec4c
--- /dev/null
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/RegionServiceAdapter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.ui.impl.topo.model;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.region.Region;
+import org.onosproject.net.region.RegionId;
+import org.onosproject.net.region.RegionListener;
+import org.onosproject.net.region.RegionService;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Adapter for {@link RegionService}.
+ */
+public class RegionServiceAdapter implements RegionService {
+    @Override
+    public void addListener(RegionListener listener) {
+    }
+
+    @Override
+    public void removeListener(RegionListener listener) {
+    }
+
+    @Override
+    public Set<Region> getRegions() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Region getRegion(RegionId regionId) {
+        return null;
+    }
+
+    @Override
+    public Region getRegionForDevice(DeviceId deviceId) {
+        return null;
+    }
+
+    @Override
+    public Set<DeviceId> getRegionDevices(RegionId regionId) {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Set<HostId> getRegionHosts(RegionId regionId) {
+        return Collections.emptySet();
+    }
+}