Added McastConnectPoint to track membership source
for egress ConnectPoints by STATIC config, PIM and
IGMP.

Change-Id: Ia913ee697e0cae32dd74db508e5ea2cba0d47c45
diff --git a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
index 36582ad..730acfa 100644
--- a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
+++ b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
@@ -17,10 +17,9 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import org.apache.commons.collections.set.ListOrderedSet;
 import org.onlab.packet.IpPrefix;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
 import org.onosproject.net.intent.SinglePointToMultiPointIntent;
 import org.onosproject.net.intent.Key;
 
@@ -34,11 +33,13 @@
     protected final IpPrefix gaddr;
     protected final IpPrefix saddr;
 
-    protected ConnectPoint ingressPoint;
-    protected Set<ConnectPoint> egressPoints;
+    protected McastConnectPoint ingressPoint;
+    protected Set<McastConnectPoint> egressPoints;
 
     protected boolean isGroup = false;
 
+    protected boolean dirty = false;
+
     /**
      * How may times has this packet been punted.
      */
@@ -160,58 +161,120 @@
     }
 
     /**
+     * Get the dirty state.
+     *
+     * @return whether this route is dirty or not.
+     */
+    public boolean getDirty() {
+        return this.dirty;
+    }
+
+    /**
+     * Set the dirty state to indicate that something changed.
+     * This may require an update to the flow tables (intents).
+     *
+     * @param dirty set the dirty bit
+     */
+    public void setDirty(boolean dirty) {
+        this.dirty = dirty;
+    }
+
+    /**
      * Add an ingress point to this route.
      *
      * @param ingress incoming connect point
+     * @return whether ingress has been added, only add if ingressPoint is null
      */
-    @Override
-    public void addIngressPoint(ConnectPoint ingress) {
-        ingressPoint = checkNotNull(ingress);
+    public boolean addIngressPoint(ConnectPoint ingress) {
+
+        // Do NOT add the ingressPoint if it is not null.
+        if (this.ingressPoint != null) {
+            // TODO: Log an warning.
+            return false;
+        }
+        this.ingressPoint = new McastConnectPoint(checkNotNull(ingress));
+        setDirty(true);
+        return true;
     }
 
     /**
      * Add or modify the ingress connect point.
      *
-     * @param deviceId the switch device Id
-     * @param portNum the ingress port number
+     * @param connectPoint string switch device Id
+     * @return whether ingress has been added, only add if ingressPoint is null
      */
-    @Override
-    public void addIngressPoint(String deviceId, long portNum) {
-        ingressPoint = new ConnectPoint(
-                DeviceId.deviceId(deviceId),
-                PortNumber.portNumber(portNum));
+    public boolean addIngressPoint(String connectPoint) {
+
+        if (this.ingressPoint != null) {
+            // TODO: log a warning.
+            return false;
+        }
+        ConnectPoint cp = ConnectPoint.deviceConnectPoint(checkNotNull(connectPoint));
+        return this.addIngressPoint(cp);
     }
 
     /**
-     * Get the ingress ConnectPoint.
+     * Get the ingress McastConnectPoint.
      *
-     * @return the ingress ConnectPoint
+     * @return the ingress McastConnectPoint
      */
-    @Override
-    public ConnectPoint getIngressPoint() {
+    public McastConnectPoint getIngressPoint() {
         return this.ingressPoint;
     }
 
     /**
-     * Add an egress ConnectPoint.
+     * Add an egress McastConnectPoint.
      *
-     * @param member member egress connect point
+     * @param cp egress connect point
+     * @return return the McastConnectPoint
      */
-    @Override
-    public void addEgressPoint(ConnectPoint member) {
-        egressPoints.add(checkNotNull(member));
+    public McastConnectPoint addEgressPoint(ConnectPoint cp) {
+        McastConnectPoint mcp = this.findEgressConnectPoint(cp);
+        if (mcp == null) {
+            mcp = new McastConnectPoint(checkNotNull(cp));
+            egressPoints.add(mcp);
+            setDirty(true);
+        }
+        return mcp;
     }
 
     /**
-     * Add an egress ConnectPoint.
+     * Add an egress connect point from a string.
      *
-     * @param deviceId deviceId of the connect point
-     * @param portNum portNum of the connect point
+     * @param connectPoint string representing a connect point
+     * @return the MulticastConnectPoint
      */
-    @Override
-    public void addEgressPoint(String deviceId, long portNum) {
-        ConnectPoint cp = new ConnectPoint(DeviceId.deviceId(deviceId), PortNumber.portNumber(portNum));
-        this.egressPoints.add(cp);
+    public McastConnectPoint addEgressPoint(String connectPoint) {
+        checkNotNull(connectPoint);
+        return this.addEgressPoint(ConnectPoint.deviceConnectPoint(connectPoint));
+    }
+
+    /**
+     * Add an egress McastConnectPoint.
+     *
+     * @param cp the egress connect point
+     * @param interest the source of interest for mcast traffic
+     */
+    public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest) {
+        checkNotNull(cp);
+        checkNotNull(interest);
+        McastConnectPoint mcp = this.addEgressPoint(cp);
+        if (mcp != null) {
+            mcp.interest.add(interest);
+            setDirty(true);
+        }
+        return mcp;
+    }
+
+    /**
+     * Add an egress McastConnectPoint.
+     *
+     * @param cpstr deviceId/port of the connect point
+     */
+    public McastConnectPoint addEgressPoint(String cpstr, McastConnectPoint.JoinSource interest) {
+        checkNotNull(cpstr);
+        checkNotNull(interest);
+        return this.addEgressPoint(ConnectPoint.deviceConnectPoint(cpstr), interest);
     }
 
     /**
@@ -219,12 +282,57 @@
      *
      * @return Set of egress connect points
      */
-    @Override
-    public Set<ConnectPoint> getEgressPoints() {
+    public Set<McastConnectPoint> getEgressPoints() {
         return egressPoints;
     }
 
     /**
+     * Get egress McastConnectPoints points as ConnectPoints for intent system.
+     *
+     * @return Set of egress ConnectPoints
+     */
+    public Set<ConnectPoint> getEgressConnectPoints() {
+        Set<ConnectPoint> cps = new ListOrderedSet();
+
+        for (McastConnectPoint mcp : egressPoints) {
+            cps.add(mcp.getConnectPoint());
+        }
+        return cps;
+    }
+
+    /**
+     * Find the Multicast Connect Point that contains the ConnectPoint.
+     *
+     * @param cp the regular ConnectPoint to match
+     * @return the McastConnectPoint that contains cp or null if not found.
+     */
+    public McastConnectPoint findEgressConnectPoint(ConnectPoint cp) {
+        for (McastConnectPoint mcp : this.egressPoints) {
+            if (mcp.getConnectPoint().equals(cp)) {
+                return mcp;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Remove specified interest from the given ConnectPoint.
+     *
+     * @param mcp connect point.
+     * @param interest the protocol interested in this multicast stream
+     * @return true if removed, false otherwise
+     */
+    public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest) {
+        checkNotNull(mcp);
+        if (mcp.interest.contains(interest)) {
+            mcp.interest.remove(interest);
+            setDirty(true);
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Get the number of times the packet has been punted.
      *
      * @return the punt count
@@ -264,7 +372,7 @@
     /**
      * Set the Intent key.
      *
-     * @param intent intent
+     * @param intent the multicast intent
      */
     @Override
     public void setIntent(SinglePointToMultiPointIntent intent) {
@@ -312,8 +420,8 @@
         out += (ingressPoint == null) ? "NULL" : ingressPoint.toString();
         out += "\n\tegress: {\n";
         if (egressPoints != null && !egressPoints.isEmpty()) {
-            for (ConnectPoint eg : egressPoints) {
-                out += "\t\t" + eg.toString() + "\n";
+            for (McastConnectPoint eg : egressPoints) {
+                out += "\t\t" + eg.getConnectPoint().toString() + "\n";
             }
         }
         out += ("\t}\n");