Refactored DestinationSet and nextobj for pws.

- Removed the booleans in destination test set and refactored
  it to use a proper enumeration.
- Create a special next objective for pseudowire transport that
  does not pop the tag.
- Install special filtering rules for receiving trafffic for
  pseudowires in all devices.

Change-Id: I665a00d9a766e026d35799fe2f12d56419cb55cd
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java
index e70ec04..3ecdd61 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java
@@ -34,59 +34,52 @@
  * paired-destination switches. May also be used to represent cases where the
  * forwarding does not use ECMP groups (ie SIMPLE next objectives)
  */
-public class DestinationSet {
+public final class DestinationSet {
     public static final int NO_EDGE_LABEL = -1;
     private static final int NOT_ASSIGNED = 0;
-    private boolean notBos; // XXX replace with enum
-    private boolean swap; // XXX replace with enum
     private final DeviceId dstSw1;
     private final int edgeLabel1;
     private final DeviceId dstSw2;
     private final int edgeLabel2;
-
+    private final DestinationSetType typeOfDstSet;
 
     private static final Logger log = getLogger(DestinationSet.class);
 
     /**
      * Constructor for a single destination with no Edge label.
      *
-     * @param isNotBos indicates if it is meant for a non bottom-of-stack label
-     * @param isSwap indicates if it is meant for a swap action
+     * @param dsType type of next objective
      * @param dstSw the destination switch
      */
-    public DestinationSet(boolean isNotBos, boolean isSwap, DeviceId dstSw) {
+    private DestinationSet(DestinationSetType dsType, DeviceId dstSw) {
         this.edgeLabel1 = NO_EDGE_LABEL;
-        this.notBos = isNotBos;
-        this.swap = isSwap;
         this.dstSw1 = dstSw;
         this.edgeLabel2 = NOT_ASSIGNED;
         this.dstSw2 = null;
+        this.typeOfDstSet = dsType;
     }
 
     /**
      * Constructor for a single destination with Edge label.
      *
-     * @param isNotBos indicates if it is meant for a non bottom-of-stack label
-     * @param isSwap indicates if it is meant for a swap action
+     * @param dsType type of next objective
      * @param edgeLabel label to be pushed as part of group operation
      * @param dstSw the destination switch
      */
-    public DestinationSet(boolean isNotBos, boolean isSwap,
-                       int edgeLabel, DeviceId dstSw) {
-        this.notBos = isNotBos;
-        this.swap = isSwap;
+    private DestinationSet(DestinationSetType dsType,
+                          int edgeLabel, DeviceId dstSw) {
         this.edgeLabel1 = edgeLabel;
         this.dstSw1 = dstSw;
         this.edgeLabel2 = NOT_ASSIGNED;
         this.dstSw2 = null;
+        this.typeOfDstSet = dsType;
     }
 
     /**
      * Constructor for paired destination switches and their associated edge
      * labels.
      *
-     * @param isNotBos indicates if it is meant for a non bottom-of-stack label
-     * @param isSwap indicates if it is meant for a swap action
+     * @param dsType type of next objective
      * @param edgeLabel1 label to be pushed as part of group operation for
      *            dstSw1
      * @param dstSw1 one of the paired destination switches
@@ -94,11 +87,9 @@
      *            dstSw2
      * @param dstSw2 the other paired destination switch
      */
-    public DestinationSet(boolean isNotBos, boolean isSwap,
+    private DestinationSet(DestinationSetType dsType,
                           int edgeLabel1, DeviceId dstSw1,
                           int edgeLabel2, DeviceId dstSw2) {
-        this.notBos = isNotBos;
-        this.swap = isSwap;
         if (dstSw1.toString().compareTo(dstSw2.toString()) <= 0) {
             this.edgeLabel1 = edgeLabel1;
             this.dstSw1 = dstSw1;
@@ -110,18 +101,17 @@
             this.edgeLabel2 = edgeLabel1;
             this.dstSw2 = dstSw1;
         }
+        this.typeOfDstSet = dsType;
     }
-
     /**
      * Default constructor for kryo serialization.
      */
-    public DestinationSet() {
+    private DestinationSet() {
         this.edgeLabel1 = NOT_ASSIGNED;
         this.edgeLabel2 = NOT_ASSIGNED;
-        this.notBos = true;
-        this.swap = true;
         this.dstSw1 = DeviceId.NONE;
         this.dstSw2 = DeviceId.NONE;
+        this.typeOfDstSet = null;
     }
 
     /**
@@ -154,21 +144,38 @@
     }
 
     /**
-     * Gets the value of notBos.
+     * Returns the type of this ds.
+     *
+     * @return the type of the destination set
+     */
+    public DestinationSetType getTypeOfDstSet() {
+        return typeOfDstSet;
+    }
+
+    /**
+     * Returns true if the next objective represented by this destination set
+     * is of type SWAP_NOT_BOS or POP_NOT_BOS.
      *
      * @return the value of notBos
      */
     public boolean notBos() {
-        return notBos;
+        if ((typeOfDstSet == DestinationSetType.SWAP_NOT_BOS) || (typeOfDstSet == DestinationSetType.POP_NOT_BOS)) {
+            return true;
+        }
+        return false;
     }
 
     /**
-     * Gets the value of swap.
+     * Returns true if the next objective represented by this destination set
+     * is of type SWAP_NOT_BOS or SWAP_BOS.
      *
      * @return the value of swap
      */
     public boolean swap() {
-        return swap;
+        if ((typeOfDstSet == DestinationSetType.SWAP_BOS) || (typeOfDstSet == DestinationSetType.SWAP_NOT_BOS)) {
+            return true;
+        }
+        return false;
     }
 
     // The list of destination ids and label are used for comparison.
@@ -181,9 +188,10 @@
             return false;
         }
         DestinationSet that = (DestinationSet) o;
+        if (this.typeOfDstSet != that.typeOfDstSet) {
+            return false;
+        }
         boolean equal = (this.edgeLabel1 == that.edgeLabel1 &&
-                            this.notBos == that.notBos &&
-                            this.swap == that.swap &&
                             this.dstSw1.equals(that.dstSw1));
         if (this.dstSw2 != null && that.dstSw2 == null ||
                 this.dstSw2 == null && that.dstSw2 != null) {
@@ -200,26 +208,16 @@
     @Override
     public int hashCode() {
         if (dstSw2 == null) {
-            return Objects.hash(notBos, swap, edgeLabel1, dstSw1);
+            return Objects.hash(typeOfDstSet, edgeLabel1, dstSw1);
         }
-        return Objects.hash(notBos, swap, edgeLabel1, dstSw1, edgeLabel2,
+        return Objects.hash(typeOfDstSet, edgeLabel1, dstSw1, edgeLabel2,
                             dstSw2);
     }
 
     @Override
     public String toString() {
-        String type;
-        if (!notBos && !swap) {
-            type = "default";
-        } else if (!notBos) {
-            type = "swapbos";
-        } else if (!swap) {
-            type = "not-bos";
-        } else {
-            type = "swap-nb";
-        }
         ToStringHelper h = toStringHelper(this)
-                                .add("Type", type)
+                                .add("Type", typeOfDstSet.getType())
                                 .add("DstSw1", dstSw1)
                                 .add("Label1", edgeLabel1);
         if (dstSw2 != null) {
@@ -228,4 +226,129 @@
         }
         return h.toString();
     }
+
+    public enum DestinationSetType {
+        /**
+         * Used to represent DestinationSetType where the next hop
+         * is the same as the final destination.
+         */
+        PUSH_NONE("pushnon"),
+        /**
+         * Used to represent DestinationSetType where we need to
+         * push a single mpls label, that of the destination.
+         */
+        PUSH_BOS("pushbos"),
+        /**
+         * Used to represent DestinationSetType where we need to pop
+         * an mpls label which has the bos bit set.
+         */
+        POP_BOS("pop-bos"),
+        /**
+         * Used to represent DestinationSetType where we swap the outer
+         * mpls label with a new one, and where the outer label has the
+         * bos bit set.
+         */
+        SWAP_BOS("swapbos"),
+        /**
+         * Used to represent DestinationSetType where we need to pop
+         * an mpls label which does not have the bos bit set.
+         */
+        POP_NOT_BOS("popnbos"),
+        /**
+         * Used to represent DestinationSetType where we swap the outer
+         * mpls label with a new one, and where the outer label does not
+         * have the bos bit set.
+         */
+        SWAP_NOT_BOS("swap-nb");
+
+        private final String typeOfDstDest;
+        DestinationSetType(String s) {
+            typeOfDstDest = s;
+        }
+
+        public String getType() {
+            return typeOfDstDest;
+        }
+    }
+
+    /*
+     * Static methods for creating DestinationSet objects in
+     * order to remove ambiquity with multiple constructors.
+     */
+
+    /**
+     * Returns a DestinationSet with type PUSH_NONE.
+     *
+     * @param destSw The deviceId for this next objective.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePushNone(DeviceId destSw) {
+        return new DestinationSet(DestinationSetType.PUSH_NONE, destSw);
+    }
+
+    /**
+     * Returns a DestinationSet with type PUSH_BOS.
+     *
+     * @param edgeLabel1 The mpls label to push.
+     * @param destSw1 The device on which the label is assigned.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePushBos(int edgeLabel1, DeviceId destSw1) {
+        return new DestinationSet(DestinationSetType.PUSH_BOS, edgeLabel1, destSw1);
+    }
+
+    /**
+     * Returns a DestinationSet with type PUSH_BOS used for paired leafs.
+     *
+     * @param edgeLabel1 The label of first paired leaf.
+     * @param destSw1 The device id of first paired leaf.
+     * @param edgeLabel2 The label of the second leaf.
+     * @param destSw2 The device id of the second leaf.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePushBos(int edgeLabel1, DeviceId destSw1, int edgeLabel2, DeviceId destSw2) {
+        return new DestinationSet(DestinationSetType.PUSH_BOS, edgeLabel1, destSw1, edgeLabel2, destSw2);
+    }
+
+    /**
+     * Returns a DestinationSet with type POP_BOS.
+     *
+     * @param deviceId The deviceId for this next objective.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePopBos(DeviceId deviceId) {
+        return new DestinationSet(DestinationSetType.POP_BOS, deviceId);
+    }
+
+    /**
+     * Returns a DestinationSet with type SWAP_BOS.
+     *
+     * @param edgeLabel The edge label to swap with.
+     * @param deviceId The deviceId for this next objective.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypeSwapBos(int edgeLabel, DeviceId deviceId) {
+        return new DestinationSet(DestinationSetType.SWAP_BOS, edgeLabel, deviceId);
+    }
+
+    /**
+     * Returns a DestinationSet with type POP_NOT_BOS.
+     *
+     * @param deviceId The device-id this next objective should be installed.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePopNotBos(DeviceId deviceId) {
+        return new DestinationSet(DestinationSetType.POP_NOT_BOS, deviceId);
+    }
+
+    /**
+     * Returns a DestinationSet with type SWAP_NOT_BOS.
+     *
+     * @param edgeLabel The edge label to swap with.
+     * @param deviceId The deviceId for this next objective.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypeSwapNotBos(int edgeLabel, DeviceId deviceId) {
+        return new DestinationSet(DestinationSetType.SWAP_NOT_BOS, edgeLabel, deviceId);
+    }
 }