ONOS-6257: fixing Region-peer-location function...

- corrected UiRegion.isRoot() implementation
- added to/from-compact-strings for LayoutLocation, so we can encode
   a list of them in an annotation
- Fixed bug in DistributedRegionStore which was emiting events that
   had a null region as subject.

Change-Id: I547e0c7f62385b85b191b8d63e6b569196623b84
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 a2c13c5..5c9b608 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
@@ -20,6 +20,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.packet.IpAddress;
 import org.onosproject.cluster.ClusterService;
@@ -55,6 +56,7 @@
 import org.onosproject.ui.model.topo.UiRegion;
 import org.onosproject.ui.model.topo.UiSynthLink;
 import org.onosproject.ui.model.topo.UiTopoLayout;
+import org.onosproject.ui.topo.LayoutLocation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -72,6 +74,7 @@
 import static org.onosproject.net.AnnotationKeys.LATITUDE;
 import static org.onosproject.net.AnnotationKeys.LONGITUDE;
 import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
+import static org.onosproject.ui.topo.LayoutLocation.fromCompactListString;
 
 /**
  * Facility for creating JSON messages to send to the topology view in the
@@ -98,6 +101,7 @@
 
     private static final String GEO = "geo";
     private static final String GRID = "grid";
+    private static final String PEER_LOCATIONS = "peerLocations";
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -342,6 +346,10 @@
         payload.set("hosts", jsonGrouped(ridStr, splitHosts));
         payload.set("layerOrder", jsonStrings(layerTags));
 
+        if (!region.isRoot()) {
+            addPeerLocations(payload, region.backingRegion());
+        }
+
         return payload;
     }
 
@@ -497,6 +505,24 @@
         }
     }
 
+    private void addPeerLocations(ObjectNode node, Region r) {
+        String compact = r.annotations().value(PEER_LOCATIONS);
+        if (!Strings.isNullOrEmpty(compact)) {
+            List<LayoutLocation> locs = fromCompactListString(compact);
+
+            ObjectNode o = objectNode();
+            for (LayoutLocation ll : locs) {
+                ObjectNode lnode = objectNode()
+                    .put("locType", ll.locType().toString())
+                    .put("latOrY", ll.latOrY())
+                    .put("longOrX", ll.longOrX());
+                o.set(ll.id(), lnode);
+            }
+
+            node.set(PEER_LOCATIONS, o);
+        }
+    }
+
     private void addIps(ObjectNode node, Host h) {
         Set<IpAddress> ips = h.ipAddresses();
 
@@ -578,7 +604,7 @@
                 .put("nHosts", region.hostCount());
         // TODO: device and host counts should take into account any nested
         //       subregions. i.e. should be the sum of all devices/hosts in
-        //       all descendent subregions.
+        //       all descendant subregions.
 
         Region r = region.backingRegion();
         // this is location data, as injected via network configuration script
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
index 3f7f4dc..3a3f141 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
@@ -229,7 +229,7 @@
         // now add the devices that reside in the parent region
         if (!layout.isRoot()) {
             UiTopoLayout parentLayout = layoutService.getLayout(layout.parent());
-            getRegion(parentLayout).devices().forEach(peers::add);
+            peers.addAll(getRegion(parentLayout).devices());
         }
 
         // TODO: Finally, filter out regions / devices that are not connected
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
index 606756b..8d7ada6 100644
--- a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/ModelCacheTest.java
@@ -25,23 +25,19 @@
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.Link;
-import org.onosproject.net.region.Region;
-import org.onosproject.ui.model.topo.UiModelEvent.Type;
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiDevice;
 import org.onosproject.ui.model.topo.UiDeviceLink;
 import org.onosproject.ui.model.topo.UiElement;
 import org.onosproject.ui.model.topo.UiHost;
 import org.onosproject.ui.model.topo.UiLinkId;
+import org.onosproject.ui.model.topo.UiModelEvent.Type;
 import org.onosproject.ui.model.topo.UiRegion;
 
 import java.util.Collection;
 import java.util.Iterator;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 import static org.onosproject.cluster.NodeId.nodeId;
 import static org.onosproject.net.PortNumber.portNumber;
 import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
@@ -223,9 +219,6 @@
         assertEquals("wrong id", REGION_2.id(), region.id());
         assertEquals("unex # device IDs", 3, region.deviceIds().size());
         assertContains("missing ID", region.deviceIds(), DEVID_4, DEVID_5, DEVID_6);
-        Region r = region.backingRegion();
-        print(r);
-        assertEquals("wrong region name", "Region-R2", r.name());
     }
 
     private static final String[] LINKS_2_7 = {D2, "27", D7, "72"};