Traffic Treatements now support deferred, immediate, table, and clear instructions.

By default, treatments are all immediate. Treatments will be deferred if the builder
predicated by deferred(). Subsequent treatments will be deferred until immediate is called
on the builder again. Multiple calls to deferred and immediate are permitted.

Change-Id: I76b3a44f2219fc1e72a7fb41b72d7bd602be85b7
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
index 75f7f7f..aa5af74 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
@@ -17,7 +17,7 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
-
+import com.google.common.collect.Lists;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
@@ -27,7 +27,6 @@
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 
@@ -36,7 +35,11 @@
  */
 public final class DefaultTrafficTreatment implements TrafficTreatment {
 
-    private final List<Instruction> instructions;
+    private final List<Instruction> immediate;
+    private final List<Instruction> deferred;
+    private final Instructions.TableTypeTransition table;
+
+    private final boolean hasClear;
 
     /**
      * Creates a new traffic treatment from the specified list of instructions.
@@ -44,12 +47,46 @@
      * @param instructions treatment instructions
      */
     private DefaultTrafficTreatment(List<Instruction> instructions) {
-        this.instructions = ImmutableList.copyOf(instructions);
+        this.immediate = ImmutableList.copyOf(instructions);
+        this.deferred = ImmutableList.of();
+        this.hasClear = false;
+        this.table = null;
+    }
+
+    private DefaultTrafficTreatment(List<Instruction> deferred,
+                                   List<Instruction> immediate,
+                                   Instructions.TableTypeTransition table,
+                                   boolean clear) {
+        this.immediate = ImmutableList.copyOf(immediate);
+        this.deferred = ImmutableList.copyOf(deferred);
+        this.table = table;
+        this.hasClear = clear;
+
     }
 
     @Override
     public List<Instruction> instructions() {
-        return instructions;
+        return immediate;
+    }
+
+    @Override
+    public List<Instruction> deferred() {
+        return deferred;
+    }
+
+    @Override
+    public List<Instruction> immediate() {
+        return immediate;
+    }
+
+    @Override
+    public Instructions.TableTypeTransition tableTransition() {
+        return table;
+    }
+
+    @Override
+    public Boolean clearedDeferred() {
+        return hasClear;
     }
 
     /**
@@ -75,7 +112,7 @@
     //FIXME: Order of instructions may affect hashcode
     @Override
     public int hashCode() {
-        return Objects.hash(instructions);
+        return Objects.hash(immediate, deferred, table);
     }
 
     @Override
@@ -85,7 +122,9 @@
         }
         if (obj instanceof DefaultTrafficTreatment) {
             DefaultTrafficTreatment that = (DefaultTrafficTreatment) obj;
-            return Objects.equals(instructions, that.instructions);
+            return Objects.equals(immediate, that.immediate) &&
+                    Objects.equals(deferred, that.deferred) &&
+                    Objects.equals(table, that.table);
 
         }
         return false;
@@ -94,7 +133,10 @@
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("instructions", instructions)
+                .add("immediate", immediate)
+                .add("deferred", deferred)
+                .add("transition", table == null ? "None" : table.toString())
+                .add("cleared", hasClear)
                 .toString();
     }
 
@@ -106,19 +148,22 @@
 
         boolean drop = false;
 
-        List<Instruction> outputs = new LinkedList<>();
+        boolean clear = false;
 
-        // TODO: should be a list of instructions based on group objects
-        List<Instruction> groups = new LinkedList<>();
+        Instructions.TableTypeTransition table;
 
-        // TODO: should be a list of instructions based on modification objects
-        List<Instruction> modifications = new LinkedList<>();
+        List<Instruction> deferred = Lists.newLinkedList();
+
+        List<Instruction> immediate = Lists.newLinkedList();
+
+        List<Instruction> current = immediate;
 
         // Creates a new builder
         private Builder() {
         }
 
         // Creates a new builder based off an existing treatment
+        //FIXME only works for immediate instruction sets.
         private Builder(TrafficTreatment treatment) {
             for (Instruction instruction : treatment.instructions()) {
                 add(instruction);
@@ -127,30 +172,26 @@
 
         @Override
         public Builder add(Instruction instruction) {
-            if (drop) {
-                return this;
-            }
+
             switch (instruction.type()) {
                 case DROP:
-                    drop = true;
-                    break;
-                case TABLE:
                 case OUTPUT:
-                    outputs.add(instruction);
-                    break;
+                case GROUP:
                 case L0MODIFICATION:
                 case L2MODIFICATION:
                 case L3MODIFICATION:
-                    // TODO: enforce modification order if any
-                    modifications.add(instruction);
+                    current.add(instruction);
                     break;
-                case GROUP:
-                    groups.add(instruction);
+                case TABLE:
+                    table = (Instructions.TableTypeTransition) instruction;
                     break;
                 default:
                     throw new IllegalArgumentException("Unknown instruction type: " +
                                                                instruction.type());
+
+
             }
+
             return this;
         }
 
@@ -254,27 +295,40 @@
         }
 
         @Override
-        public TrafficTreatment.Builder transition(FlowRule.Type type) {
-            return add(Instructions.transition(type));
-        }
-
-        @Override
         public Builder popVlan() {
             return add(Instructions.popVlan());
         }
 
         @Override
+        public Builder transition(FlowRule.Type type) {
+            return add(Instructions.transition(type));
+        }
+
+        @Override
+        public Builder immediate() {
+            current = immediate;
+            return this;
+        }
+
+        @Override
+        public Builder deferred() {
+            current = deferred;
+            return this;
+        }
+
+        @Override
+        public Builder wipeDeferred() {
+            clear = true;
+            return this;
+        }
+
+        @Override
         public TrafficTreatment build() {
-
-            //If we are dropping should we just return an empty list?
-            List<Instruction> instructions = new LinkedList<Instruction>();
-            instructions.addAll(modifications);
-            instructions.addAll(groups);
-            if (!drop) {
-                instructions.addAll(outputs);
+            if (deferred.size() == 0 && immediate.size() == 0
+                    && table == null && !clear) {
+                drop();
             }
-
-            return new DefaultTrafficTreatment(instructions);
+            return new DefaultTrafficTreatment(deferred, immediate, table, clear);
         }
 
     }