Implemented initial loading of ModelCache.
Created UiLinkId to canonicalize identifiers for UI links, based on src and dst elements.
Added idAsString() and name() methods to UiElement.
Added toString() to UiDevice, UiLink, UiHost.
Created Mock services for testing.

Change-Id: I4d27110e5aca08f29bb719f17e9ec65d6786e2c8
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopology.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopology.java
index a8b5b06..3a11fcf 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopology.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopology.java
@@ -17,27 +17,54 @@
 package org.onosproject.ui.model.topo;
 
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.region.RegionId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
-import java.util.TreeSet;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
 
 /**
  * Represents the overall network topology.
  */
 public class UiTopology extends UiElement {
 
+    private static final String E_UNMAPPED =
+            "Attempting to retrieve unmapped {}: {}";
+
     private static final String DEFAULT_TOPOLOGY_ID = "TOPOLOGY-0";
 
     private static final Logger log = LoggerFactory.getLogger(UiTopology.class);
 
-    private final UiCluster uiCluster = new UiCluster();
-    private final Set<UiRegion> uiRegions = new TreeSet<>();
+
+    // top level mappings of topology elements by ID
+    private final Map<NodeId, UiClusterMember> cnodeLookup = new HashMap<>();
+    private final Map<RegionId, UiRegion> regionLookup = new HashMap<>();
+    private final Map<DeviceId, UiDevice> deviceLookup = new HashMap<>();
+    private final Map<HostId, UiHost> hostLookup = new HashMap<>();
+    private final Map<UiLinkId, UiLink> linkLookup = new HashMap<>();
+
 
     @Override
     public String toString() {
-        return "Topology: " + uiCluster + ", " + uiRegions.size() + " regions";
+        return toStringHelper(this)
+                .add("#cnodes", clusterMemberCount())
+                .add("#regions", regionCount())
+                .add("#devices", deviceLookup.size())
+                .add("#hosts", hostLookup.size())
+                .add("#links", linkLookup.size())
+                .toString();
+    }
+
+    @Override
+    public String idAsString() {
+        return DEFAULT_TOPOLOGY_ID;
     }
 
     /**
@@ -46,19 +73,22 @@
      */
     public void clear() {
         log.debug("clearing topology model");
-        uiRegions.clear();
-        uiCluster.clear();
+        cnodeLookup.clear();
+        regionLookup.clear();
+        deviceLookup.clear();
+        hostLookup.clear();
+        linkLookup.clear();
     }
 
     /**
      * Returns the cluster member with the given identifier, or null if no
-     * such member.
+     * such member exists.
      *
      * @param id cluster node identifier
-     * @return the cluster member with that identifier
+     * @return corresponding UI cluster member
      */
     public UiClusterMember findClusterMember(NodeId id) {
-        return uiCluster.find(id);
+        return cnodeLookup.get(id);
     }
 
     /**
@@ -67,7 +97,7 @@
      * @param member cluster member to add
      */
     public void add(UiClusterMember member) {
-        uiCluster.add(member);
+        cnodeLookup.put(member.id(), member);
     }
 
     /**
@@ -76,7 +106,10 @@
      * @param member cluster member to remove
      */
     public void remove(UiClusterMember member) {
-        uiCluster.remove(member);
+        UiClusterMember m = cnodeLookup.remove(member.id());
+        if (m != null) {
+            m.destroy();
+        }
     }
 
     /**
@@ -85,7 +118,18 @@
      * @return number of cluster members
      */
     public int clusterMemberCount() {
-        return uiCluster.size();
+        return cnodeLookup.size();
+    }
+
+    /**
+     * Returns the region with the specified identifier, or null if
+     * no such region exists.
+     *
+     * @param id region identifier
+     * @return corresponding UI region
+     */
+    public UiRegion findRegion(RegionId id) {
+        return regionLookup.get(id);
     }
 
     /**
@@ -94,11 +138,182 @@
      * @return number of regions
      */
     public int regionCount() {
-        return uiRegions.size();
+        return regionLookup.size();
     }
 
-    @Override
-    public String idAsString() {
-        return DEFAULT_TOPOLOGY_ID;
+    /**
+     * Adds the given region to the topology model.
+     *
+     * @param uiRegion region to add
+     */
+    public void add(UiRegion uiRegion) {
+        regionLookup.put(uiRegion.id(), uiRegion);
     }
+
+    /**
+     * Removes the given region from the topology model.
+     *
+     * @param uiRegion region to remove
+     */
+    public void remove(UiRegion uiRegion) {
+        regionLookup.remove(uiRegion.id());
+    }
+
+    /**
+     * Returns the device with the specified identifier, or null if
+     * no such device exists.
+     *
+     * @param id device identifier
+     * @return corresponding UI device
+     */
+    public UiDevice findDevice(DeviceId id) {
+        return deviceLookup.get(id);
+    }
+
+    /**
+     * Adds the given device to the topology model.
+     *
+     * @param uiDevice device to add
+     */
+    public void add(UiDevice uiDevice) {
+        deviceLookup.put(uiDevice.id(), uiDevice);
+    }
+
+    /**
+     * Removes the given device from the topology model.
+     *
+     * @param uiDevice device to remove
+     */
+    public void remove(UiDevice uiDevice) {
+        UiDevice d = deviceLookup.remove(uiDevice.id());
+        if (d != null) {
+            d.destroy();
+        }
+    }
+
+    /**
+     * Returns the link with the specified identifier, or null if no such
+     * link exists.
+     *
+     * @param id the canonicalized link identifier
+     * @return corresponding UI link
+     */
+    public UiLink findLink(UiLinkId id) {
+        return linkLookup.get(id);
+    }
+
+    /**
+     * Adds the given UI link to the topology model.
+     *
+     * @param uiLink link to add
+     */
+    public void add(UiLink uiLink) {
+        linkLookup.put(uiLink.id(), uiLink);
+    }
+
+    /**
+     * Removes the given UI link from the model.
+     *
+     * @param uiLink link to remove
+     */
+    public void remove(UiLink uiLink) {
+        UiLink link = linkLookup.get(uiLink.id());
+        if (link != null) {
+            link.destroy();
+        }
+    }
+
+    /**
+     * Returns the host with the specified identifier, or null if no such
+     * host exists.
+     *
+     * @param id host identifier
+     * @return corresponding UI host
+     */
+    public UiHost findHost(HostId id) {
+        return hostLookup.get(id);
+    }
+
+    /**
+     * Adds the given host to the topology model.
+     *
+     * @param uiHost host to add
+     */
+    public void add(UiHost uiHost) {
+        hostLookup.put(uiHost.id(), uiHost);
+    }
+
+    /**
+     * Removes the given host from the topology model.
+     *
+     * @param uiHost host to remove
+     */
+    public void remove(UiHost uiHost) {
+        UiHost h = hostLookup.remove(uiHost.id());
+        if (h != null) {
+            h.destroy();
+        }
+    }
+
+    // ==
+    // package private methods for supporting linkage amongst topology entities
+    // ==
+
+    /**
+     * Returns the set of UI devices with the given identifiers.
+     *
+     * @param deviceIds device identifiers
+     * @return set of matching UI device instances
+     */
+    Set<UiDevice> deviceSet(Set<DeviceId> deviceIds) {
+        Set<UiDevice> uiDevices = new HashSet<>();
+        for (DeviceId id : deviceIds) {
+            UiDevice d = deviceLookup.get(id);
+            if (d != null) {
+                uiDevices.add(d);
+            } else {
+                log.warn(E_UNMAPPED, "device", id);
+            }
+        }
+        return uiDevices;
+    }
+
+    /**
+     * Returns the set of UI hosts with the given identifiers.
+     *
+     * @param hostIds host identifiers
+     * @return set of matching UI host instances
+     */
+    Set<UiHost> hostSet(Set<HostId> hostIds) {
+        Set<UiHost> uiHosts = new HashSet<>();
+        for (HostId id : hostIds) {
+            UiHost h = hostLookup.get(id);
+            if (h != null) {
+                uiHosts.add(h);
+            } else {
+                log.warn(E_UNMAPPED, "host", id);
+            }
+        }
+        return uiHosts;
+    }
+
+    /**
+     * Returns the set of UI links with the given identifiers.
+     *
+     * @param uiLinkIds link identifiers
+     * @return set of matching UI link instances
+     */
+    Set<UiLink> linkSet(Set<UiLinkId> uiLinkIds) {
+        Set<UiLink> uiLinks = new HashSet<>();
+        for (UiLinkId id : uiLinkIds) {
+            UiLink link = linkLookup.get(id);
+            if (link != null) {
+                uiLinks.add(link);
+            } else {
+                log.warn(E_UNMAPPED, "link", id);
+            }
+        }
+        return uiLinks;
+    }
+
 }