Implement IntentCompilers

For the following Intent types

- PointToPointIntent
- PathIntent
- MultiPointToSinglePointIntent

This resolves ONOS-1657, ONOS-1659, and ONOS-1660.

Change-Id: I7c68cec4b025a59f9c97bae4488801bea2716baf
diff --git a/src/main/java/net/onrc/onos/core/newintent/MultiPointToSinglePointIntentCompiler.java b/src/main/java/net/onrc/onos/core/newintent/MultiPointToSinglePointIntentCompiler.java
new file mode 100644
index 0000000..eb31457
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/newintent/MultiPointToSinglePointIntentCompiler.java
@@ -0,0 +1,111 @@
+package net.onrc.onos.core.newintent;
+
+import net.onrc.onos.api.flowmanager.FlowIdGenerator;
+import net.onrc.onos.api.flowmanager.SingleDstTreeFlow;
+import net.onrc.onos.api.flowmanager.Tree;
+import net.onrc.onos.api.newintent.Intent;
+import net.onrc.onos.api.newintent.IntentIdGenerator;
+import net.onrc.onos.api.newintent.MultiPointToSinglePointIntent;
+import net.onrc.onos.core.intent.ConstrainedBFSTree;
+import net.onrc.onos.core.intent.Path;
+import net.onrc.onos.core.matchaction.match.Match;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.topology.BaseTopology;
+import net.onrc.onos.core.topology.ITopologyService;
+import net.onrc.onos.core.topology.Switch;
+import net.onrc.onos.core.util.SwitchPort;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static net.onrc.onos.core.newintent.PointToPointIntentCompiler.convertPath;
+
+/**
+ * An intent compiler for {@link MultiPointToSinglePointIntent}.
+ */
+public class MultiPointToSinglePointIntentCompiler
+        extends AbstractFlowGeneratingIntentCompiler<MultiPointToSinglePointIntent> {
+
+    private final ITopologyService topologyService;
+
+    /**
+     * Constructs an intent compiler for {@link MultiPointToSinglePointIntent}.
+     *
+     * @param intentIdGenerator intent ID generator
+     * @param flowIdGenerator flow ID generator
+     * @param topologyService topology service
+     */
+    public MultiPointToSinglePointIntentCompiler(IntentIdGenerator intentIdGenerator,
+                                                 FlowIdGenerator flowIdGenerator,
+                                                 ITopologyService topologyService) {
+        super(intentIdGenerator, flowIdGenerator);
+        this.topologyService = checkNotNull(topologyService);
+    }
+
+    @Override
+    public List<Intent> compile(MultiPointToSinglePointIntent intent) {
+        Match match = intent.getMatch();
+        if (!(match instanceof PacketMatch)) {
+            throw new IntentCompilationException(
+                    "intent has unsupported type of match object: " + match
+            );
+        }
+
+        SingleDstTreeFlow treeFlow = new SingleDstTreeFlow(
+                getNextFlowId(),
+                (PacketMatch) intent.getMatch(), // down-cast, but it is safe due to the guard above
+                intent.getIngressPorts(),
+                calculateTree(intent.getIngressPorts(), intent.getEgressPort()),
+                packActions(intent, intent.getEgressPort())
+        );
+        Intent compiled = new SingleDstTreeFlowIntent(getNextId(), treeFlow);
+        return Arrays.asList(compiled);
+    }
+
+    /**
+     * Calculates a tree with the specified ingress ports and egress port.
+     *
+     * {@link PathNotFoundException} is thrown when no tree found or
+     * the specified egress port is not found in the topology.
+     *
+     * @param ingressPorts ingress ports
+     * @param egressPort egress port
+     * @return tree
+     * @throws PathNotFoundException if the specified egress switch is not
+     * found or no tree is found.
+     */
+    private Tree calculateTree(Set<SwitchPort> ingressPorts, SwitchPort egressPort) {
+        BaseTopology topology = topologyService.getTopology();
+        Switch egressSwitch = topology.getSwitch(egressPort.getDpid());
+        if (egressSwitch == null) {
+            throw new PathNotFoundException("destination switch not found: " + egressPort.getDpid());
+        }
+
+        ConstrainedBFSTree bfs = new ConstrainedBFSTree(egressSwitch);
+        Tree tree = new Tree();
+
+        for (SwitchPort ingressPort: ingressPorts) {
+            Switch ingressSwitch = topology.getSwitch(ingressPort.getDpid());
+            if (ingressSwitch == null) {
+                continue;
+            }
+
+            Path path = bfs.getPath(ingressSwitch);
+            if (path.isEmpty()) {
+                continue;
+            }
+
+            tree.addAll(convertPath(path));
+        }
+
+        if (tree.isEmpty()) {
+            throw new PathNotFoundException(
+                    String.format("No tree found (ingress: %s, egress: %s", ingressPorts, egressPort)
+            );
+        }
+
+        return tree;
+    }
+}