ONOS-6258: UiTopo2Overlay et al.
 - initial support for topo-2 highlighting.

Change-Id: I71c61b902047153ea420a8b2ecd89f6950daa4a9
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index 00295c4..c302c6b 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -28,9 +28,12 @@
 import org.onosproject.ui.UiExtensionService;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiTopo2OverlayFactory;
 import org.onosproject.ui.UiTopoLayoutService;
 import org.onosproject.ui.UiTopoOverlayFactory;
 import org.onosproject.ui.impl.topo.Topo2Jsonifier;
+import org.onosproject.ui.impl.topo.Topo2OverlayCache;
+import org.onosproject.ui.impl.topo.Topo2ViewMessageHandler;
 import org.onosproject.ui.impl.topo.UiTopoSession;
 import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
 import org.onosproject.ui.model.topo.UiTopoLayout;
@@ -50,7 +53,6 @@
     private static final Logger log = LoggerFactory.getLogger(UiWebSocket.class);
 
     private static final String EVENT = "event";
-    private static final String SID = "sid";
     private static final String PAYLOAD = "payload";
     private static final String UNKNOWN = "unknown";
 
@@ -81,6 +83,7 @@
 
     private Map<String, UiMessageHandler> handlers;
     private TopoOverlayCache overlayCache;
+    private Topo2OverlayCache overlay2Cache;
 
     /**
      * Creates a new web-socket for serving data to the Web UI.
@@ -190,7 +193,7 @@
         topoSession.destroy();
         destroyHandlersAndOverlays();
         log.info("GUI client disconnected [close-code={}, message={}]",
-                closeCode, message);
+                 closeCode, message);
     }
 
     @Override
@@ -244,6 +247,7 @@
         log.debug("Creating handlers and overlays...");
         handlers = new HashMap<>();
         overlayCache = new TopoOverlayCache();
+        overlay2Cache = new Topo2OverlayCache();
 
         UiExtensionService service = directory.get(UiExtensionService.class);
         service.getExtensions().forEach(ext -> {
@@ -255,10 +259,13 @@
                         handler.messageTypes().forEach(type -> handlers.put(type, handler));
 
                         // need to inject the overlay cache into topology message handler
-                        // TODO: code for Topo2ViewMessageHandler required here
                         if (handler instanceof TopologyViewMessageHandler) {
                             ((TopologyViewMessageHandler) handler).setOverlayCache(overlayCache);
                         }
+
+                        if (handler instanceof Topo2ViewMessageHandler) {
+                            ((Topo2ViewMessageHandler) handler).setOverlayCache(overlay2Cache);
+                        }
                     } catch (Exception e) {
                         log.warn("Unable to setup handler {} due to", handler, e);
                     }
@@ -269,9 +276,14 @@
             if (overlayFactory != null) {
                 overlayFactory.newOverlays().forEach(overlayCache::add);
             }
+
+            UiTopo2OverlayFactory overlay2Factory = ext.topo2OverlayFactory();
+            if (overlay2Factory != null) {
+                overlay2Factory.newOverlays().forEach(overlay2Cache::add);
+            }
         });
         log.debug("#handlers = {}, #overlays = {}", handlers.size(),
-                overlayCache.size());
+                  overlayCache.size());
     }
 
     // Destroys message handlers.
@@ -298,7 +310,7 @@
                     .put(ID, node.id().toString())
                     .put(IP, node.ip().toString())
                     .put(GlyphConstants.UI_ATTACHED,
-                            node.equals(service.getLocalNode()));
+                         node.equals(service.getLocalNode()));
             instances.add(instance);
         }
 
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2OverlayCache.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2OverlayCache.java
new file mode 100644
index 0000000..1c05acf
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2OverlayCache.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017-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;
+
+import org.onosproject.ui.UiTopo2Overlay;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * A cache of {@link org.onosproject.ui.UiTopo2Overlay}'s that were
+ * registered at the time the UI connection was established.
+ * <p>
+ * Note, for now, this is a simplified version which will only cache
+ * a single overlay. At some future point, this should be expanded to mirror
+ * the behavior of {@link org.onosproject.ui.impl.TopoOverlayCache}.
+ */
+public class Topo2OverlayCache {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String EMPTY = "";
+    private static final String NO_OVERLAY = "No Overlay";
+    private static final String UNKNOWN = "unknown";
+
+    private static final UiTopo2Overlay NONE = new NullOverlay();
+
+    private final Map<String, UiTopo2Overlay> overlays = new HashMap<>();
+    private UiTopo2Overlay current = null;
+
+    /**
+     * Constructs the overlay cache.
+     */
+    public Topo2OverlayCache() {
+        overlays.put(null, NONE);
+    }
+
+    /**
+     * Adds a topology-2 overlay to the cache.
+     *
+     * @param overlay a topology-2 overlay
+     */
+    public void add(UiTopo2Overlay overlay) {
+        overlays.put(overlay.id(), overlay);
+        log.warn("added overlay: " + overlay);
+    }
+
+    /**
+     * Invoked when the cache is no longer needed.
+     */
+    public void destroy() {
+        overlays.clear();
+    }
+
+    /**
+     * Switching currently selected overlay.
+     *
+     * @param deact identity of overlay to deactivate
+     * @param act   identity of overlay to activate
+     */
+    public void switchOverlay(String deact, String act) {
+        UiTopo2Overlay toDeactivate = getOverlay(deact);
+        UiTopo2Overlay toActivate = getOverlay(act);
+
+        toDeactivate.deactivate();
+        current = toActivate;
+        current.activate();
+    }
+
+    private UiTopo2Overlay getOverlay(String id) {
+        return isNullOrEmpty(id) ? NONE : overlays.get(id);
+    }
+
+    /**
+     * Returns the current overlay instance.
+     * Note that this method always returns a reference; when there is no
+     * overlay selected the "NULL" overlay instance is returned.
+     *
+     * @return the current overlay
+     */
+    public UiTopo2Overlay currentOverlay() {
+        return current;
+    }
+
+    /**
+     * Returns the number of overlays in the cache. Remember that this
+     * includes the "NULL" overlay, representing "no overlay selected".
+     *
+     * @return number of overlays
+     */
+    public int size() {
+        return overlays.size();
+    }
+
+    /**
+     * Returns true if the identifier of the currently active overlay
+     * matches the given parameter.
+     *
+     * @param overlayId overlay identifier
+     * @return true if this matches the ID of currently active overlay
+     */
+    public boolean isActive(String overlayId) {
+        return currentOverlay().id().equals(overlayId);
+    }
+
+    /**
+     * Returns the collection of registered overlays.
+     *
+     * @return registered overlays
+     */
+    public Collection<UiTopo2Overlay> list() {
+        return overlays.values();
+    }
+
+    // overlay instance representing "no overlay selected"
+    private static class NullOverlay extends UiTopo2Overlay {
+        NullOverlay() {
+            super(EMPTY, NO_OVERLAY);
+        }
+
+        @Override
+        public String glyphId() {
+            return UNKNOWN;
+        }
+    }
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
index 3c93110..1763048 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
@@ -16,12 +16,14 @@
 
 package org.onosproject.ui.impl.topo;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import org.onlab.osgi.ServiceDirectory;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiConnection;
 import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.UiTopo2Overlay;
 import org.onosproject.ui.impl.UiWebSocket;
 import org.onosproject.ui.model.topo.UiClusterMember;
 import org.onosproject.ui.model.topo.UiNode;
@@ -67,10 +69,12 @@
     private static final String CURRENT_LAYOUT = "topo2CurrentLayout";
     private static final String CURRENT_REGION = "topo2CurrentRegion";
     private static final String PEER_REGIONS = "topo2PeerRegions";
+    private static final String OVERLAYS = "topo2Overlays";
 
 
     private UiTopoSession topoSession;
     private Topo2Jsonifier t2json;
+    private Topo2OverlayCache overlay2Cache;
 
 
     @Override
@@ -82,6 +86,17 @@
         t2json = new Topo2Jsonifier(directory, connection.userName());
     }
 
+    /**
+     * Sets a reference to the overlay cache for interacting with registered
+     * overlays.
+     *
+     * @param overlay2Cache the overlay cache
+     */
+    public void setOverlayCache(Topo2OverlayCache overlay2Cache) {
+        this.overlay2Cache = overlay2Cache;
+    }
+
+
     @Override
     protected Collection<RequestHandler> createRequestHandlers() {
         return ImmutableSet.of(
@@ -114,6 +129,25 @@
         return peersPayload;
     }
 
+    private ObjectNode mkOverlaysMessage() {
+        ArrayNode a = arrayNode();
+        for (UiTopo2Overlay ov : overlay2Cache.list()) {
+            a.add(json(ov));
+        }
+        ObjectNode payload = objectNode();
+        payload.set("overlays", a);
+        return payload;
+    }
+
+    private ObjectNode json(UiTopo2Overlay ov) {
+        return objectNode()
+                .put("id", ov.id())
+                .put("name", ov.name())
+                .put("gid", ov.glyphId());
+    }
+
+    // ==================================================================
+
 
     private final class Topo2Start extends RequestHandler {
         private Topo2Start() {
@@ -152,6 +186,9 @@
 
             // these are the regions/devices that are siblings to this region
             sendMessage(PEER_REGIONS, mkPeersMessage(currentLayout));
+
+            // these are the registered overlays
+            sendMessage(OVERLAYS, mkOverlaysMessage());
         }
     }