Add MatchActionOperations computation mechanism.

- Added MatchActionOperations computation mechanism converting from the Flow batch operation.
- Currently supporting ADD operations only.
- Currently supporting PacketPathFlow and SingleDstTreeFlow objects only.
- This task is a preparation for ONOS-1690 and ONOS-1692.

Change-Id: I7b98ee0b58c7edf27642b4e369cc99e12ab5293a
diff --git a/src/main/java/net/onrc/onos/core/flowmanager/FlowManagerModule.java b/src/main/java/net/onrc/onos/core/flowmanager/FlowManagerModule.java
index 235e38a..c9263b2 100644
--- a/src/main/java/net/onrc/onos/core/flowmanager/FlowManagerModule.java
+++ b/src/main/java/net/onrc/onos/core/flowmanager/FlowManagerModule.java
@@ -1,14 +1,26 @@
 package net.onrc.onos.core.flowmanager;
 
-import java.util.Collection;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import net.onrc.onos.api.batchoperation.BatchOperationEntry;
 import net.onrc.onos.api.flowmanager.ConflictDetectionPolicy;
 import net.onrc.onos.api.flowmanager.Flow;
 import net.onrc.onos.api.flowmanager.FlowBatchHandle;
 import net.onrc.onos.api.flowmanager.FlowBatchOperation;
+import net.onrc.onos.api.flowmanager.FlowBatchOperation.Operator;
 import net.onrc.onos.api.flowmanager.FlowId;
 import net.onrc.onos.api.flowmanager.FlowManagerListener;
 import net.onrc.onos.api.flowmanager.FlowManagerService;
+import net.onrc.onos.core.matchaction.MatchActionIdGeneratorWithIdBlockAllocator;
+import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
+import net.onrc.onos.core.matchaction.MatchActionOperations;
+import net.onrc.onos.core.matchaction.MatchActionOperationsIdGeneratorWithIdBlockAllocator;
+import net.onrc.onos.core.util.IdBlockAllocator;
 
 /**
  * Manages a set of Flow objects, computes and maintains a set of Match-Action
@@ -19,11 +31,17 @@
 public class FlowManagerModule implements FlowManagerService {
     private ConflictDetectionPolicy conflictDetectionPolicy;
     private FlowOperationMap flowOperationMap;
+    private MatchActionIdGeneratorWithIdBlockAllocator maIdAllocator;
+    private MatchActionOperationsIdGeneratorWithIdBlockAllocator maoIdAllocator;
 
     /**
-     * Constructor.
+     * Constructs FlowManagerModule with {@link IdBlockAllocator}.
      */
-    public FlowManagerModule() {
+    public FlowManagerModule(IdBlockAllocator idBlockAllocator) {
+        this.maIdAllocator =
+                new MatchActionIdGeneratorWithIdBlockAllocator(idBlockAllocator);
+        this.maoIdAllocator =
+                new MatchActionOperationsIdGeneratorWithIdBlockAllocator(idBlockAllocator);
         this.conflictDetectionPolicy = ConflictDetectionPolicy.FREE;
         this.flowOperationMap = new FlowOperationMap();
     }
@@ -54,16 +72,23 @@
         return null;
     }
 
+    /**
+     * Executes batch operation of Flow object asynchronously.
+     * <p>
+     * To track the execution result, use the returned FlowBatchHandle object.
+     * <p>
+     * This method just put the batch-operation object to the global flow
+     * operation map with unique ID. The worker process for execution and
+     * installation will get the appended operation when it gets events from the
+     * map. This method returns a handler for obtaining the result of this
+     * operation, control the executing process, etc.
+     *
+     * @param ops flow operations to be executed
+     * @return FlowBatchHandle object if succeeded, null otherwise
+     */
     @Override
     public FlowBatchHandle executeBatch(FlowBatchOperation ops) {
-        // This method just put the batch-operation object to the global
-        // flow operation map with unique ID. The leader process will get
-        // the appended operation with events notified from the map.
-        FlowBatchHandle handler = flowOperationMap.putOperation(ops);
-
-        // Then it will return a handler to obtain the result of this operation,
-        // control the executing process, etc.
-        return handler;
+        return flowOperationMap.putOperation(ops);
     }
 
     @Override
@@ -92,4 +117,55 @@
         // TODO Auto-generated method stub
 
     }
+
+    private MatchActionOperations createNewMatchActionOperations() {
+        return new MatchActionOperations(maoIdAllocator.getNewId());
+    }
+
+    /**
+     * Generates the series of MatchActionOperations from the
+     * {@link FlowBatchOperation}.
+     * <p>
+     * Note: Currently supporting ADD operations only.
+     * <p>
+     * Note: Currently supporting PacketPathFlow and SingleDstTreeFlow only.
+     *
+     * @param op the {@link FlowBatchOperation} object
+     * @return the list of {@link MatchActionOperations} objects
+     */
+    private List<MatchActionOperations>
+            generateMatchActionOperationsList(FlowBatchOperation op) {
+        MatchActionOperations firstOps = createNewMatchActionOperations();
+        MatchActionOperations secondOps = createNewMatchActionOperations();
+
+        for (BatchOperationEntry<Operator, ?> e : op.getOperations()) {
+            if (e.getOperator() != FlowBatchOperation.Operator.ADD) {
+                throw new UnsupportedOperationException(
+                        "FlowManager supports ADD operations only.");
+            }
+            if (!(e.getTarget() instanceof Flow)) {
+                throw new IllegalStateException(
+                        "The target is not Flow object: " + e.getTarget());
+            }
+
+            Flow flow = (Flow) e.getTarget();
+            List<MatchActionOperations> maOps = flow.compile(
+                    e.getOperator(), maIdAllocator, maoIdAllocator);
+            checkNotNull(maOps, "Could not compile the flow: " + flow);
+            checkState(maOps.size() == 2,
+                    "The flow generates unspported match-action operations.");
+
+            for (MatchActionOperationEntry mae : maOps.get(0).getOperations()) {
+                checkState(mae.getOperator() == MatchActionOperations.Operator.ADD);
+                firstOps.addOperation(mae);
+            }
+
+            for (MatchActionOperationEntry mae : maOps.get(1).getOperations()) {
+                checkState(mae.getOperator() == MatchActionOperations.Operator.ADD);
+                secondOps.addOperation(mae);
+            }
+        }
+
+        return Arrays.asList(firstOps, secondOps);
+    }
 }
diff --git a/src/test/java/net/onrc/onos/core/flowmanager/FlowManagerModuleTest.java b/src/test/java/net/onrc/onos/core/flowmanager/FlowManagerModuleTest.java
index 3467812..39a4d05 100644
--- a/src/test/java/net/onrc/onos/core/flowmanager/FlowManagerModuleTest.java
+++ b/src/test/java/net/onrc/onos/core/flowmanager/FlowManagerModuleTest.java
@@ -2,6 +2,8 @@
 
 import static org.junit.Assert.assertEquals;
 import net.onrc.onos.api.flowmanager.ConflictDetectionPolicy;
+import net.onrc.onos.core.registry.StandaloneRegistry;
+import net.onrc.onos.core.util.IdBlockAllocator;
 
 import org.junit.After;
 import org.junit.Before;
@@ -10,11 +12,14 @@
 import org.junit.rules.ExpectedException;
 
 public class FlowManagerModuleTest {
+    IdBlockAllocator idBlockAllocator;
+
     @Rule
     public ExpectedException thrown = ExpectedException.none();
 
     @Before
     public void setUp() throws Exception {
+        idBlockAllocator = new StandaloneRegistry();
     }
 
     @After
@@ -22,12 +27,12 @@
     }
 
     /**
-     * Checks the default conflict detection policy is the FREE policy, and
-     * the other policies are not supported.
+     * Checks the default conflict detection policy is the FREE policy, and the
+     * other policies are not supported.
      */
     @Test
     public void testConflictDetectionPolicy() {
-        FlowManagerModule flowManager = new FlowManagerModule();
+        FlowManagerModule flowManager = new FlowManagerModule(idBlockAllocator);
         assertEquals(ConflictDetectionPolicy.FREE,
                 flowManager.getConflictDetectionPolicy());