Move indices to TopologyImpl.

Change-Id: I3efd73129fafb786e959810e8239ce14d26af6d7
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
index 2bba015..5ce3729 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
@@ -1,5 +1,6 @@
 package net.onrc.onos.core.topology;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -15,12 +16,21 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+
 public class TopologyImpl implements Topology {
     @SuppressWarnings("unused")
     private static final Logger log = LoggerFactory.getLogger(TopologyImpl.class);
 
     // DPID -> Switch
     private final ConcurrentMap<Dpid, Switch> switches;
+    // XXX may need to be careful when shallow copying.
+    private final ConcurrentMap<Dpid, ConcurrentMap<PortNumber, Port>> ports;
+
+    // Index from Port to Device
+    private final Multimap<SwitchPort, Device> devices;
     private final ConcurrentMap<MACAddress, Device> mac2Device;
 
     private final ConcurrentMap<SwitchPort, Link> outgoingLinks;
@@ -34,6 +44,9 @@
     public TopologyImpl() {
         // TODO: Does these object need to be stored in Concurrent Collection?
         switches = new ConcurrentHashMap<>();
+        ports = new ConcurrentHashMap<>();
+        devices = Multimaps.synchronizedMultimap(
+                HashMultimap.<SwitchPort, Device>create());
         mac2Device = new ConcurrentHashMap<>();
         outgoingLinks = new ConcurrentHashMap<>();
         incomingLinks = new ConcurrentHashMap<>();
@@ -45,16 +58,43 @@
         return switches.get(dpid);
     }
 
+    // Only add switch.
     protected void putSwitch(Switch sw) {
         switches.put(sw.getDpid(), sw);
+        ports.putIfAbsent(sw.getDpid(), new ConcurrentHashMap<PortNumber, Port>());
     }
 
+    // TODO remove me when ready
     protected void removeSwitch(Long dpid) {
-        switches.remove(new Dpid(dpid));
+        removeSwitch(new Dpid(dpid));
     }
 
+    // XXX Will remove ports in snapshot as side-effect.
     protected void removeSwitch(Dpid dpid) {
         switches.remove(dpid);
+        ports.remove(dpid);
+    }
+
+    // This method is expected to be serialized by writeLock.
+    protected void putPort(Port port) {
+        ConcurrentMap<PortNumber, Port> portMap = ports.get(port.getDpid());
+        if (portMap == null) {
+            portMap = new ConcurrentHashMap<>();
+            ConcurrentMap<PortNumber, Port> existing =
+                    ports.putIfAbsent(port.getDpid(), portMap);
+            if (existing != null) {
+                // port map was added concurrently, using theirs
+                portMap = existing;
+            }
+        }
+        portMap.put(port.getNumber(), port);
+    }
+
+    protected void removePort(Port port) {
+        ConcurrentMap<PortNumber, Port> portMap = ports.get(port.getDpid());
+        if (portMap != null) {
+            portMap.remove(port.getNumber());
+        }
     }
 
     @Override
@@ -65,9 +105,9 @@
 
     @Override
     public Port getPort(Dpid dpid, PortNumber number) {
-        Switch sw = getSwitch(dpid);
-        if (sw != null) {
-            return sw.getPort(number);
+        ConcurrentMap<PortNumber, Port> portMap = ports.get(dpid);
+        if (portMap != null) {
+            return portMap.get(number);
         }
         return null;
     }
@@ -78,6 +118,15 @@
     }
 
     @Override
+    public Collection<Port> getPorts(Dpid dpid) {
+        ConcurrentMap<PortNumber, Port> portMap = ports.get(dpid);
+        if (portMap == null) {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableCollection(portMap.values());
+    }
+
+    @Override
     public Link getOutgoingLink(Dpid dpid, PortNumber number) {
         return outgoingLinks.get(new SwitchPort(dpid, number));
     }
@@ -139,11 +188,34 @@
         return Collections.unmodifiableCollection(mac2Device.values());
     }
 
+    @Override
+    public Collection<Device> getDevices(SwitchPort port) {
+        return Collections.unmodifiableCollection(devices.get(port));
+    }
+
+    // This method is expected to be serialized by writeLock.
+    // XXX new or updated device
     protected void putDevice(Device device) {
+        // assuming Device is immutable
+        Device oldDevice = mac2Device.get(device.getMacAddress());
+        if (oldDevice != null) {
+            // remove old attachment point
+            removeDevice(oldDevice);
+        }
+        // add new attachment points
+        for (Port port : device.getAttachmentPoints()) {
+            // TODO Won't need remove() if we define Device equality to reflect
+            //      all of it's fields.
+            devices.remove(port.asSwitchPort(), device);
+            devices.put(port.asSwitchPort(), device);
+        }
         mac2Device.put(device.getMacAddress(), device);
     }
 
     protected void removeDevice(Device device) {
+        for (Port port : device.getAttachmentPoints()) {
+            devices.remove(port.asSwitchPort(), device);
+        }
         mac2Device.remove(device.getMacAddress());
     }