Layout and Region configs.
- Listen for topo-layout config changes.
- Augmenting UiTopoLayout to include fields for geomap/sprite, scale/offset

Change-Id: I2b1f747f41d39b64b0a1a53946c4cbd5750db9e5
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 5e6927f..193191c 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
@@ -23,12 +23,11 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
-import org.onlab.util.KryoNamespace;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.basics.BasicUiTopoLayoutConfig;
 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;
@@ -37,6 +36,7 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -54,38 +54,34 @@
 @Service
 public class UiTopoLayoutManager implements UiTopoLayoutService {
 
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
     private static final String ID_NULL = "Layout ID cannot be null";
     private static final String LAYOUT_NULL = "Layout cannot be null";
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected StorageService storageService;
+    private final Logger log = LoggerFactory.getLogger(getClass());
 
-    private ConsistentMap<UiTopoLayoutId, UiTopoLayout> layouts;
-    private Map<UiTopoLayoutId, UiTopoLayout> layoutMap;
+    private final InternalConfigListener cfgListener = new InternalConfigListener();
+    private final Map<UiTopoLayoutId, UiTopoLayout> layoutMap = new HashMap<>();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry cfgService;
+
 
     @Activate
     public void activate() {
-        KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
-                .register(KryoNamespaces.API)
-                .register(UiTopoLayout.class);
-
-        layouts = storageService.<UiTopoLayoutId, UiTopoLayout>consistentMapBuilder()
-                .withSerializer(Serializer.using(kryoBuilder.build()))
-                .withName("onos-topo-layouts")
-                .withRelaxedReadConsistency()
-                .build();
-        layoutMap = layouts.asJavaMap();
 
         // Create and add the default layout, if needed.
-        layoutMap.computeIfAbsent(DEFAULT_ID, k -> new UiTopoLayout(k, null, null));
+        layoutMap.computeIfAbsent(DEFAULT_ID, UiTopoLayout::new);
+
+        cfgService.addListener(cfgListener);
+        cfgListener.initAllConfigs();
 
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
+        cfgService.removeListener(cfgListener);
+
         log.info("Stopped");
     }
 
@@ -103,7 +99,7 @@
     @Override
     public boolean addLayout(UiTopoLayout layout) {
         checkNotNull(layout, LAYOUT_NULL);
-        return layouts.put(layout.id(), layout) == null;
+        return layoutMap.put(layout.id(), layout) == null;
     }
 
     @Override
@@ -153,7 +149,51 @@
     @Override
     public boolean removeLayout(UiTopoLayout layout) {
         checkNotNull(layout, LAYOUT_NULL);
-        return layouts.remove(layout.id()) != null;
+        return layoutMap.remove(layout.id()) != null;
     }
 
+    /*
+     * Listens for changes to layout configs, updating instances as necessary
+     */
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        // look up the current config by layout ID and apply it
+        private void updateLayoutConfig(UiTopoLayoutId id) {
+            BasicUiTopoLayoutConfig cfg =
+                    cfgService.getConfig(id, BasicUiTopoLayoutConfig.class);
+
+            log.info("Updating Layout via config... {}: {}", id, cfg);
+
+            UiTopoLayout layout = layoutMap.get(id);
+
+            // NOTE: if a value is null, then that null-ness should be set
+            // TODO: add setters on UiTopoLayout and implement...
+//            layout
+//              .region(cfg.region())
+//              .parent(cfg.parent())
+//              .geomap(cfg.geomap())
+//              .sprites(cfg.sprites())
+//              .scale(cfg.scale())
+//              .offsetX(cfg.offsetX())
+//              .offsetY(cfg.offsetY());
+        }
+
+        private void initAllConfigs() {
+            log.info("Initializing layout configurations...");
+            layoutMap.keySet().forEach(this::updateLayoutConfig);
+        }
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            UiTopoLayoutId id = (UiTopoLayoutId) event.subject();
+            updateLayoutConfig(id);
+        }
+
+        @Override
+        public boolean isRelevant(NetworkConfigEvent event) {
+            return (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+                    event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+                    event.configClass().equals(BasicUiTopoLayoutConfig.class);
+        }
+    }
 }
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/UiTopoLayoutManagerTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/UiTopoLayoutManagerTest.java
index 10ceb8a..67fbf69 100644
--- a/web/gui/src/test/java/org/onosproject/ui/impl/topo/UiTopoLayoutManagerTest.java
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/UiTopoLayoutManagerTest.java
@@ -19,45 +19,62 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
 import org.onosproject.net.region.DefaultRegion;
 import org.onosproject.net.region.Region;
-import org.onosproject.net.region.RegionId;
-import org.onosproject.store.service.TestStorageService;
 import org.onosproject.ui.UiTopoLayoutService;
 import org.onosproject.ui.model.topo.UiTopoLayout;
 import org.onosproject.ui.model.topo.UiTopoLayoutId;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.onosproject.net.region.Region.Type.CAMPUS;
+import static org.onosproject.net.region.RegionId.regionId;
+import static org.onosproject.ui.model.topo.UiTopoLayoutId.layoutId;
 
 /**
  * Suite of unit tests for the UI topology layout manager.
  */
 public class UiTopoLayoutManagerTest {
 
+    private static class MockConfigService extends NetworkConfigRegistryAdapter {
+    }
+
+    private static Region region(String id, String name, Region.Type type) {
+        return new DefaultRegion(regionId(id), name, type, null);
+    }
+
+    private static UiTopoLayout layout(String id, Region region, String parentId) {
+        UiTopoLayoutId parent = parentId == null ? null : layoutId(parentId);
+        UiTopoLayout layout = new UiTopoLayout(layoutId(id));
+        // TODO: set region and parent
+        return layout;
+    }
+
+    private static final Region R1 = region("r1", "R1", CAMPUS);
+    private static final Region R2 = region("r2", "R2", CAMPUS);
+
+    private static final UiTopoLayout L1 = layout("l1", R1, null);
+    private static final UiTopoLayout L2 = layout("l2", R2, null);
+
+
     private UiTopoLayoutService svc;
     private UiTopoLayoutManager mgr;
 
-    private static final UiTopoLayout L1 =
-            new UiTopoLayout(UiTopoLayoutId.layoutId("l1"),
-                             new DefaultRegion(RegionId.regionId("r1"), "R1",
-                                               Region.Type.CAMPUS, null), null);
-    private static final UiTopoLayout L2 =
-            new UiTopoLayout(UiTopoLayoutId.layoutId("l2"),
-                             new DefaultRegion(RegionId.regionId("r2"), "R2",
-                                               Region.Type.CAMPUS, null), null);
 
     @Before
     public void setUp() {
         mgr = new UiTopoLayoutManager();
         svc = mgr;
-        mgr.storageService = new TestStorageService();
+
+        mgr.cfgService = new MockConfigService();
         mgr.activate();
     }
 
     @After
     public void tearDown() {
         mgr.deactivate();
-        mgr.storageService = null;
+        mgr.cfgService = null;
     }
 
     @Test
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 ea48bb1..559a6be 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
@@ -263,7 +263,7 @@
                                          String parentId) {
         UiTopoLayoutId pid = parentId == null
                 ? UiTopoLayoutId.DEFAULT_ID : layoutId(parentId);
-        return new UiTopoLayout(layoutId(layoutId), region, pid);
+        return new UiTopoLayout(layoutId(layoutId)).region(region).parent(pid);
     }
 
     /**