Fix [ONOS-4857] and impement [ONOS-5143]

Change-Id: I7159ff9deaacaf10070e1535f4a80f2f846a5784
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
new file mode 100644
index 0000000..9fbedb9
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.SetMultimap;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L1ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
+import org.onosproject.net.flow.instructions.L4ModificationInstruction;
+import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
+import org.onosproject.net.intent.IntentCompilationException;
+import org.onosproject.net.intent.LinkCollectionIntent;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Shared APIs and implementations for Link Collection compilers.
+ */
+public class LinkCollectionCompiler<T> {
+
+    /**
+     * Helper class to encapsulate treatment and selector.
+     */
+    protected class ForwardingInstructions {
+
+        private TrafficTreatment trafficTreatment;
+
+        private TrafficSelector trafficSelector;
+
+        public ForwardingInstructions(TrafficTreatment treatment, TrafficSelector selector) {
+
+            this.trafficTreatment = treatment;
+            this.trafficSelector = selector;
+
+        }
+
+        public TrafficTreatment treatment() {
+            return this.trafficTreatment;
+        }
+
+        public TrafficSelector selector() {
+            return this.trafficSelector;
+        }
+
+    }
+
+    /**
+     * Helper method to compute input and ouput ports.
+     *
+     * @param intent the related intents
+     * @param inputPorts the input ports to compute
+     * @param outputPorts the output ports to compute
+     */
+    protected void computePorts(LinkCollectionIntent intent,
+                                SetMultimap<DeviceId, PortNumber> inputPorts,
+                                SetMultimap<DeviceId, PortNumber> outputPorts) {
+
+        for (Link link : intent.links()) {
+            inputPorts.put(link.dst().deviceId(), link.dst().port());
+            outputPorts.put(link.src().deviceId(), link.src().port());
+        }
+
+        for (ConnectPoint ingressPoint : intent.ingressPoints()) {
+            inputPorts.put(ingressPoint.deviceId(), ingressPoint.port());
+        }
+
+        for (ConnectPoint egressPoint : intent.egressPoints()) {
+            outputPorts.put(egressPoint.deviceId(), egressPoint.port());
+        }
+
+    }
+
+    /**
+     * Helper method to compute ingress and egress ports.
+     *
+     * @param intent the related intents
+     * @param ingressPorts the ingress ports to compute
+     * @param egressPorts the egress ports to compute
+     */
+    protected void computePorts(LinkCollectionIntent intent,
+                                DeviceId deviceId,
+                                Set<PortNumber> ingressPorts,
+                                Set<PortNumber> egressPorts) {
+
+        if (!intent.applyTreatmentOnEgress()) {
+            ingressPorts.addAll(intent.ingressPoints().stream()
+                    .filter(point -> point.deviceId().equals(deviceId))
+                    .map(ConnectPoint::port)
+                    .collect(Collectors.toSet()));
+        } else {
+            egressPorts.addAll(intent.egressPoints().stream()
+                    .filter(point -> point.deviceId().equals(deviceId))
+                    .map(ConnectPoint::port)
+                    .collect(Collectors.toSet()));
+        }
+
+    }
+
+    /**
+     * Creates the flows representations.
+     *
+     * @param intent the intent to compile
+     * @param deviceId the affected device
+     * @param inPorts the input ports
+     * @param outPorts the output ports
+     * @return the list of flows representations
+     */
+    protected List<T> createRules(LinkCollectionIntent intent, DeviceId deviceId,
+                                       Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
+        return null;
+    }
+
+
+    /**
+     * Computes treatment and selector which will be used
+     * in the flow representation (Rule, Objective).
+     *
+     * @param intent the intent to compile
+     * @param inPort the input port
+     * @param outPorts the output ports
+     * @param ingressPorts the ingress ports
+     * @param egressPorts the egress ports
+     * @return the forwarding instruction object which encapsulates treatment and selector
+     */
+    protected ForwardingInstructions createForwardingInstructions(LinkCollectionIntent intent, PortNumber inPort,
+                                                                  Set<PortNumber> outPorts,
+                                                                  Set<PortNumber> ingressPorts,
+                                                                  Set<PortNumber> egressPorts) {
+
+        TrafficTreatment.Builder defaultTreatmentBuilder = DefaultTrafficTreatment.builder();
+        outPorts.forEach(defaultTreatmentBuilder::setOutput);
+        TrafficTreatment outputOnlyTreatment = defaultTreatmentBuilder.build();
+        TrafficSelector.Builder selectorBuilder;
+        TrafficTreatment treatment;
+        TrafficTreatment intentTreatment;
+
+        if (!intent.applyTreatmentOnEgress()) {
+            TrafficTreatment.Builder ingressTreatmentBuilder = DefaultTrafficTreatment.builder(intent.treatment());
+            outPorts.forEach(ingressTreatmentBuilder::setOutput);
+            intentTreatment = ingressTreatmentBuilder.build();
+
+            if (ingressPorts.contains(inPort)) {
+                selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
+                treatment = intentTreatment;
+            } else {
+                selectorBuilder = this.createSelectorFromFwdInstructions(
+                        new ForwardingInstructions(intentTreatment, intent.selector())
+                );
+                treatment = outputOnlyTreatment;
+            }
+        } else {
+            if (outPorts.stream().allMatch(egressPorts::contains)) {
+                TrafficTreatment.Builder egressTreatmentBuilder =
+                        DefaultTrafficTreatment.builder(intent.treatment());
+                outPorts.forEach(egressTreatmentBuilder::setOutput);
+
+                selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
+                treatment = egressTreatmentBuilder.build();
+            } else {
+                selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
+                treatment = outputOnlyTreatment;
+            }
+        }
+
+        TrafficSelector selector = selectorBuilder.matchInPort(inPort).build();
+
+        return new ForwardingInstructions(treatment, selector);
+
+    }
+
+    /**
+     * Update the selector builder using a L0 instruction.
+     *
+     * @param builder the builder to update
+     * @param l0instruction the l0 instruction to use
+     */
+    private void updateBuilder(TrafficSelector.Builder builder, L0ModificationInstruction l0instruction) {
+        throw new IntentCompilationException("L0 not supported");
+    }
+
+    /**
+     * Update the selector builder using a L1 instruction.
+     *
+     * @param builder the builder to update
+     * @param l1instruction the l1 instruction to use
+     */
+    private void updateBuilder(TrafficSelector.Builder builder, L1ModificationInstruction l1instruction) {
+        throw new IntentCompilationException("L1 not supported");
+    }
+
+    /**
+     * Update the selector builder using a L2 instruction.
+     *
+     * @param builder the builder to update
+     * @param l2instruction the l2 instruction to use
+     */
+    private void updateBuilder(TrafficSelector.Builder builder, L2ModificationInstruction l2instruction) {
+        switch (l2instruction.subtype()) {
+            case ETH_SRC:
+            case ETH_DST:
+                ModEtherInstruction ethInstr = (ModEtherInstruction) l2instruction;
+                switch (ethInstr.subtype()) {
+                    case ETH_SRC:
+                        builder.matchEthSrc(ethInstr.mac());
+                        break;
+                    case ETH_DST:
+                        builder.matchEthDst(ethInstr.mac());
+                        break;
+                    default:
+                        throw new IntentCompilationException("Bad eth subtype");
+                }
+                break;
+            case VLAN_ID:
+                ModVlanIdInstruction vlanIdInstr = (ModVlanIdInstruction) l2instruction;
+                builder.matchVlanId(vlanIdInstr.vlanId());
+                break;
+            case VLAN_PUSH:
+                //FIXME
+                break;
+            case VLAN_POP:
+                //TODO how do we handle dropped label? remove the selector?
+                throw new IntentCompilationException("Can't handle pop label");
+            case VLAN_PCP:
+                ModVlanPcpInstruction vlanPcpInstruction = (ModVlanPcpInstruction) l2instruction;
+                builder.matchVlanPcp(vlanPcpInstruction.vlanPcp());
+                break;
+            case MPLS_LABEL:
+            case MPLS_PUSH:
+                //FIXME
+                ModMplsLabelInstruction mplsInstr = (ModMplsLabelInstruction) l2instruction;
+                builder.matchMplsLabel(mplsInstr.label());
+                break;
+            case MPLS_POP:
+                //TODO how do we handle dropped label? remove the selector?
+                throw new IntentCompilationException("Can't handle pop label");
+            case DEC_MPLS_TTL:
+                // no-op
+                break;
+            case MPLS_BOS:
+                ModMplsBosInstruction mplsBosInstr = (ModMplsBosInstruction) l2instruction;
+                builder.matchMplsBos(mplsBosInstr.mplsBos());
+                break;
+            case TUNNEL_ID:
+                ModTunnelIdInstruction tunInstr = (ModTunnelIdInstruction) l2instruction;
+                builder.matchTunnelId(tunInstr.tunnelId());
+                break;
+            default:
+                throw new IntentCompilationException("Unknown L2 Modification instruction");
+        }
+
+    }
+
+    /**
+     * Update the selector builder using a L3 instruction.
+     *
+     * @param builder the builder to update
+     * @param l3instruction the l3 instruction to use
+     */
+    private void updateBuilder(TrafficSelector.Builder builder, L3ModificationInstruction l3instruction) {
+        // TODO check ethernet proto
+        switch (l3instruction.subtype()) {
+            case IPV4_SRC:
+            case IPV4_DST:
+            case IPV6_SRC:
+            case IPV6_DST:
+                ModIPInstruction ipInstr = (ModIPInstruction) l3instruction;
+                // TODO check if ip falls in original prefix
+                IpPrefix prefix = ipInstr.ip().toIpPrefix();
+                switch (ipInstr.subtype()) {
+                    case IPV4_SRC:
+                        builder.matchIPSrc(prefix);
+                        break;
+                    case IPV4_DST:
+                        builder.matchIPSrc(prefix);
+                        break;
+                    case IPV6_SRC:
+                        builder.matchIPv6Src(prefix);
+                        break;
+                    case IPV6_DST:
+                        builder.matchIPv6Dst(prefix);
+                        break;
+                    default:
+                        throw new IntentCompilationException("Bad type for IP instruction");
+                }
+                break;
+            case IPV6_FLABEL:
+                ModIPv6FlowLabelInstruction ipFlowInstr = (ModIPv6FlowLabelInstruction) l3instruction;
+                builder.matchIPv6FlowLabel(ipFlowInstr.flowLabel());
+                break;
+            case DEC_TTL:
+                // no-op
+                break;
+            case TTL_OUT:
+                // no-op
+                break;
+            case TTL_IN:
+                // no-op
+                break;
+            case ARP_SPA:
+                ModArpIPInstruction arpIpInstr = (ModArpIPInstruction) l3instruction;
+                if (arpIpInstr.ip().isIp4()) {
+                    builder.matchArpSpa((Ip4Address) arpIpInstr.ip());
+                } else {
+                    throw new IntentCompilationException("IPv6 not supported for ARP");
+                }
+                break;
+            case ARP_SHA:
+                ModArpEthInstruction arpEthInstr = (ModArpEthInstruction) l3instruction;
+                builder.matchArpSha(arpEthInstr.mac());
+                break;
+            case ARP_OP:
+                ModArpOpInstruction arpOpInstr = (ModArpOpInstruction) l3instruction;
+                //FIXME is the long to int cast safe?
+                builder.matchArpOp((int) arpOpInstr.op());
+                break;
+            default:
+                throw new IntentCompilationException("Unknown L3 Modification instruction");
+        }
+    }
+
+    /**
+     * Update the selector builder using a L4 instruction.
+     *
+     * @param builder the builder to update
+     * @param l4instruction the l4 instruction to use
+     */
+    private void updateBuilder(TrafficSelector.Builder builder, L4ModificationInstruction l4instruction) {
+        if (l4instruction instanceof ModTransportPortInstruction) {
+            // TODO check IP proto
+            ModTransportPortInstruction l4mod = (ModTransportPortInstruction) l4instruction;
+            switch (l4mod.subtype()) {
+                case TCP_SRC:
+                    builder.matchTcpSrc(l4mod.port());
+                    break;
+                case TCP_DST:
+                    builder.matchTcpDst(l4mod.port());
+                    break;
+                case UDP_SRC:
+                    builder.matchUdpSrc(l4mod.port());
+                    break;
+                case UDP_DST:
+                    builder.matchUdpDst(l4mod.port());
+                    break;
+                default:
+                    throw new IntentCompilationException("Unknown L4 Modification instruction");
+            }
+        } else {
+            throw new IntentCompilationException("Unknown L4 Modification instruction");
+        }
+    }
+
+    /**
+     * Computes the new traffic selector using the
+     * forwarding instructions.
+     *
+     * @param fwInstructions it encapsulates the instructions to compute the new selector
+     * @return the traffic selector builder
+     */
+    private TrafficSelector.Builder createSelectorFromFwdInstructions(ForwardingInstructions fwInstructions) {
+            TrafficSelector.Builder defaultSelectorBuilder = DefaultTrafficSelector.builder(fwInstructions.selector());
+            fwInstructions.treatment().allInstructions().forEach(instruction -> {
+                switch (instruction.type()) {
+                    case L0MODIFICATION:
+                        updateBuilder(defaultSelectorBuilder, (L0ModificationInstruction) instruction);
+                        break;
+                    case L1MODIFICATION:
+                        updateBuilder(defaultSelectorBuilder, (L1ModificationInstruction) instruction);
+                        break;
+                    case L2MODIFICATION:
+                        updateBuilder(defaultSelectorBuilder, (L2ModificationInstruction) instruction);
+                        break;
+                    case L3MODIFICATION:
+                        updateBuilder(defaultSelectorBuilder, (L3ModificationInstruction) instruction);
+                        break;
+                    case L4MODIFICATION:
+                        updateBuilder(defaultSelectorBuilder, (L4ModificationInstruction) instruction);
+                        break;
+                    case NOACTION:
+                    case OUTPUT:
+                    case GROUP:
+                    case QUEUE:
+                    case TABLE:
+                    case METER:
+                    case METADATA:
+                    case EXTENSION: // TODO is extension no-op or unsupported?
+                        // Nothing to do
+                        break;
+                    default:
+                        throw new IntentCompilationException("Unknown instruction type");
+                }
+            });
+            return defaultSelectorBuilder;
+    }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
index 53c4287..7c2157c 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
@@ -16,43 +16,22 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpPrefix;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpEthInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpIPInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModArpOpInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
-import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
-import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentCompilationException;
 import org.onosproject.net.intent.IntentCompiler;
 import org.onosproject.net.intent.LinkCollectionIntent;
 
@@ -60,10 +39,14 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 
+/**
+ * Compiler to produce flow rules from link collections.
+ */
 @Component(immediate = true)
-public class LinkCollectionIntentCompiler implements IntentCompiler<LinkCollectionIntent> {
+public class LinkCollectionIntentCompiler
+        extends LinkCollectionCompiler<FlowRule>
+        implements IntentCompiler<LinkCollectionIntent> {
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentConfigurableRegistrator registrator;
@@ -86,21 +69,11 @@
 
     @Override
     public List<Intent> compile(LinkCollectionIntent intent, List<Intent> installable) {
+
         SetMultimap<DeviceId, PortNumber> inputPorts = HashMultimap.create();
         SetMultimap<DeviceId, PortNumber> outputPorts = HashMultimap.create();
 
-        for (Link link : intent.links()) {
-            inputPorts.put(link.dst().deviceId(), link.dst().port());
-            outputPorts.put(link.src().deviceId(), link.src().port());
-        }
-
-        for (ConnectPoint ingressPoint : intent.ingressPoints()) {
-            inputPorts.put(ingressPoint.deviceId(), ingressPoint.port());
-        }
-
-        for (ConnectPoint egressPoint : intent.egressPoints()) {
-            outputPorts.put(egressPoint.deviceId(), egressPoint.port());
-        }
+        computePorts(intent, inputPorts, outputPorts);
 
         List<FlowRule> rules = new ArrayList<>();
         for (DeviceId deviceId: outputPorts.keys()) {
@@ -109,235 +82,37 @@
         return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources()));
     }
 
-    private List<FlowRule> createRules(LinkCollectionIntent intent, DeviceId deviceId,
+    @Override
+    protected List<FlowRule> createRules(LinkCollectionIntent intent, DeviceId deviceId,
                                        Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
-        TrafficTreatment.Builder defaultTreatmentBuilder = DefaultTrafficTreatment.builder();
-        outPorts.forEach(defaultTreatmentBuilder::setOutput);
-        TrafficTreatment outputOnlyTreatment = defaultTreatmentBuilder.build();
-        Set<PortNumber> ingressPorts = Collections.emptySet();
-        Set<PortNumber> egressPorts = Collections.emptySet();
 
-        if (!intent.applyTreatmentOnEgress()) {
-            ingressPorts = intent.ingressPoints().stream()
-                    .filter(point -> point.deviceId().equals(deviceId))
-                    .map(ConnectPoint::port)
-                    .collect(Collectors.toSet());
-        } else {
-            egressPorts = intent.egressPoints().stream()
-                    .filter(point -> point.deviceId().equals(deviceId))
-                    .map(ConnectPoint::port)
-                    .collect(Collectors.toSet());
-        }
+        Set<PortNumber> ingressPorts = Sets.newHashSet();
+        Set<PortNumber> egressPorts = Sets.newHashSet();
+
+        computePorts(intent, deviceId, ingressPorts, egressPorts);
 
         List<FlowRule> rules = new ArrayList<>(inPorts.size());
-        for (PortNumber inPort: inPorts) {
-            TrafficSelector.Builder selectorBuilder;
-            TrafficTreatment treatment;
-            TrafficTreatment intentTreatment;
+        Set<PortNumber> copyIngressPorts = ImmutableSet.copyOf(ingressPorts);
+        Set<PortNumber> copyEgressPorts = ImmutableSet.copyOf(egressPorts);
 
-            if (!intent.applyTreatmentOnEgress()) {
-                TrafficTreatment.Builder ingressTreatmentBuilder = DefaultTrafficTreatment.builder(intent.treatment());
-                outPorts.forEach(ingressTreatmentBuilder::setOutput);
-                intentTreatment = ingressTreatmentBuilder.build();
-
-                if (ingressPorts.contains(inPort)) {
-                    selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
-                    treatment = intentTreatment;
-                } else {
-                    selectorBuilder = applyTreatmentToSelector(intent.selector(), intentTreatment);
-                    treatment = outputOnlyTreatment;
-                }
-            } else {
-                if (outPorts.stream().allMatch(egressPorts::contains)) {
-                    TrafficTreatment.Builder egressTreatmentBuilder =
-                            DefaultTrafficTreatment.builder(intent.treatment());
-                    outPorts.forEach(egressTreatmentBuilder::setOutput);
-
-                    selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
-                    treatment = egressTreatmentBuilder.build();
-                } else {
-                    selectorBuilder = DefaultTrafficSelector.builder(intent.selector());
-                    treatment = outputOnlyTreatment;
-                }
+        inPorts.forEach(inport -> {
+                ForwardingInstructions instructions = this.createForwardingInstructions(intent,
+                                                                                        inport,
+                                                                                        outPorts,
+                                                                                        copyIngressPorts,
+                                                                                        copyEgressPorts);
+                FlowRule rule = DefaultFlowRule.builder()
+                        .forDevice(deviceId)
+                        .withSelector(instructions.selector())
+                        .withTreatment(instructions.treatment())
+                        .withPriority(intent.priority())
+                        .fromApp(appId)
+                        .makePermanent()
+                        .build();
+                rules.add(rule);
             }
-            TrafficSelector selector = selectorBuilder.matchInPort(inPort).build();
-
-            FlowRule rule = DefaultFlowRule.builder()
-                    .forDevice(deviceId)
-                    .withSelector(selector)
-                    .withTreatment(treatment)
-                    .withPriority(intent.priority())
-                    .fromApp(appId)
-                    .makePermanent()
-                    .build();
-            rules.add(rule);
-        }
+        );
 
         return rules;
     }
-
-    private TrafficSelector.Builder applyTreatmentToSelector(TrafficSelector selector, TrafficTreatment treatment) {
-        TrafficSelector.Builder defaultSelectorBuilder = DefaultTrafficSelector.builder(selector);
-        treatment.allInstructions().forEach(instruction -> {
-            switch (instruction.type()) {
-                case L0MODIFICATION:
-                case L1MODIFICATION:
-                    throw new IntentCompilationException("L0 and L1 mods not supported");
-                case L2MODIFICATION:
-                    L2ModificationInstruction l2mod = (L2ModificationInstruction) instruction;
-                    switch (l2mod.subtype()) {
-                        case ETH_SRC:
-                        case ETH_DST:
-                            ModEtherInstruction ethInstr = (ModEtherInstruction) l2mod;
-                            switch (ethInstr.subtype()) {
-                                case ETH_SRC:
-                                    defaultSelectorBuilder.matchEthSrc(ethInstr.mac());
-                                    break;
-                                case ETH_DST:
-                                    defaultSelectorBuilder.matchEthDst(ethInstr.mac());
-                                    break;
-                                default:
-                                    throw new IntentCompilationException("Bad eth subtype");
-                            }
-                            break;
-                        case VLAN_ID:
-                            ModVlanIdInstruction vlanIdInstr = (ModVlanIdInstruction) l2mod;
-                            defaultSelectorBuilder.matchVlanId(vlanIdInstr.vlanId());
-                            break;
-                        case VLAN_PUSH:
-                            //FIXME
-                            break;
-                        case VLAN_POP:
-                            //TODO how do we handle dropped label? remove the selector?
-                            throw new IntentCompilationException("Can't handle pop label");
-                        case VLAN_PCP:
-                            ModVlanPcpInstruction vlanPcpInstruction = (ModVlanPcpInstruction) l2mod;
-                            defaultSelectorBuilder.matchVlanPcp(vlanPcpInstruction.vlanPcp());
-                            break;
-                        case MPLS_LABEL:
-                        case MPLS_PUSH:
-                            //FIXME
-                            ModMplsLabelInstruction mplsInstr = (ModMplsLabelInstruction) l2mod;
-                            defaultSelectorBuilder.matchMplsLabel(mplsInstr.label());
-                            break;
-                        case MPLS_POP:
-                            //TODO how do we handle dropped label? remove the selector?
-                            throw new IntentCompilationException("Can't handle pop label");
-                        case DEC_MPLS_TTL:
-                            // no-op
-                            break;
-                        case MPLS_BOS:
-                            ModMplsBosInstruction mplsBosInstr = (ModMplsBosInstruction) l2mod;
-                            defaultSelectorBuilder.matchMplsBos(mplsBosInstr.mplsBos());
-                            break;
-                        case TUNNEL_ID:
-                            ModTunnelIdInstruction tunInstr = (ModTunnelIdInstruction) l2mod;
-                            defaultSelectorBuilder.matchTunnelId(tunInstr.tunnelId());
-                            break;
-                        default:
-                            throw new IntentCompilationException("Unknown L2 Modification instruction");
-                    }
-                    break;
-                case L3MODIFICATION:
-                    L3ModificationInstruction l3mod = (L3ModificationInstruction) instruction;
-                    // TODO check ethernet proto
-                    switch (l3mod.subtype()) {
-                        case IPV4_SRC:
-                        case IPV4_DST:
-                        case IPV6_SRC:
-                        case IPV6_DST:
-                            ModIPInstruction ipInstr = (ModIPInstruction) l3mod;
-                            // TODO check if ip falls in original prefix
-                            IpPrefix prefix = ipInstr.ip().toIpPrefix();
-                            switch (ipInstr.subtype()) {
-                                case IPV4_SRC:
-                                    defaultSelectorBuilder.matchIPSrc(prefix);
-                                    break;
-                                case IPV4_DST:
-                                    defaultSelectorBuilder.matchIPSrc(prefix);
-                                    break;
-                                case IPV6_SRC:
-                                    defaultSelectorBuilder.matchIPv6Src(prefix);
-                                    break;
-                                case IPV6_DST:
-                                    defaultSelectorBuilder.matchIPv6Dst(prefix);
-                                    break;
-                                default:
-                                    throw new IntentCompilationException("Bad type for IP instruction");
-                            }
-                            break;
-                        case IPV6_FLABEL:
-                            ModIPv6FlowLabelInstruction ipFlowInstr = (ModIPv6FlowLabelInstruction) l3mod;
-                            defaultSelectorBuilder.matchIPv6FlowLabel(ipFlowInstr.flowLabel());
-                            break;
-                        case DEC_TTL:
-                            // no-op
-                            break;
-                        case TTL_OUT:
-                            // no-op
-                            break;
-                        case TTL_IN:
-                            // no-op
-                            break;
-                        case ARP_SPA:
-                            ModArpIPInstruction arpIpInstr = (ModArpIPInstruction) l3mod;
-                            if (arpIpInstr.ip().isIp4()) {
-                                defaultSelectorBuilder.matchArpSpa((Ip4Address) arpIpInstr.ip());
-                            } else {
-                                throw new IntentCompilationException("IPv6 not supported for ARP");
-                            }
-                            break;
-                        case ARP_SHA:
-                            ModArpEthInstruction arpEthInstr = (ModArpEthInstruction) l3mod;
-                            defaultSelectorBuilder.matchArpSha(arpEthInstr.mac());
-                            break;
-                        case ARP_OP:
-                            ModArpOpInstruction arpOpInstr = (ModArpOpInstruction) l3mod;
-                            //FIXME is the long to int cast safe?
-                            defaultSelectorBuilder.matchArpOp((int) arpOpInstr.op());
-                            break;
-                        default:
-                            throw new IntentCompilationException("Unknown L3 Modification instruction");
-                    }
-                    break;
-                case L4MODIFICATION:
-                    if (instruction instanceof ModTransportPortInstruction) {
-                        // TODO check IP proto
-                        ModTransportPortInstruction l4mod = (ModTransportPortInstruction) instruction;
-                        switch (l4mod.subtype()) {
-                            case TCP_SRC:
-                                defaultSelectorBuilder.matchTcpSrc(l4mod.port());
-                                break;
-                            case TCP_DST:
-                                defaultSelectorBuilder.matchTcpDst(l4mod.port());
-                                break;
-                            case UDP_SRC:
-                                defaultSelectorBuilder.matchUdpSrc(l4mod.port());
-                                break;
-                            case UDP_DST:
-                                defaultSelectorBuilder.matchUdpDst(l4mod.port());
-                                break;
-                            default:
-                                throw new IntentCompilationException("Unknown L4 Modification instruction");
-                        }
-                    } else {
-                        throw new IntentCompilationException("Unknown L4 Modification instruction");
-                    }
-                    break;
-                case NOACTION:
-                case OUTPUT:
-                case GROUP:
-                case QUEUE:
-                case TABLE:
-                case METER:
-                case METADATA:
-                case EXTENSION: // TODO is extension no-op or unsupported?
-                    // Nothing to do
-                    break;
-                default:
-                    throw new IntentCompilationException("Unknown instruction type");
-            }
-        });
-        return defaultSelectorBuilder;
-    }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectivesCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
similarity index 67%
rename from core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectivesCompiler.java
rename to core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
index 09c7037a..7ffff6b 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectivesCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentFlowObjectiveCompiler.java
@@ -16,7 +16,9 @@
 package org.onosproject.net.intent.impl.compiler;
 
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -24,14 +26,8 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
 import org.onosproject.net.flowobjective.DefaultNextObjective;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
@@ -47,13 +43,14 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * Compiler to produce flow objectives from link collections.
  */
 @Component(immediate = true)
-public class LinkCollectionIntentFlowObjectivesCompiler implements IntentCompiler<LinkCollectionIntent> {
+public class LinkCollectionIntentFlowObjectiveCompiler
+        extends LinkCollectionCompiler<Objective>
+        implements IntentCompiler<LinkCollectionIntent> {
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentConfigurableRegistrator registrator;
@@ -79,21 +76,11 @@
 
     @Override
     public List<Intent> compile(LinkCollectionIntent intent, List<Intent> installable) {
+
         SetMultimap<DeviceId, PortNumber> inputPorts = HashMultimap.create();
         SetMultimap<DeviceId, PortNumber> outputPorts = HashMultimap.create();
 
-        for (Link link : intent.links()) {
-            inputPorts.put(link.dst().deviceId(), link.dst().port());
-            outputPorts.put(link.src().deviceId(), link.src().port());
-        }
-
-        for (ConnectPoint ingressPoint : intent.ingressPoints()) {
-            inputPorts.put(ingressPoint.deviceId(), ingressPoint.port());
-        }
-
-        for (ConnectPoint egressPoint : intent.egressPoints()) {
-            outputPorts.put(egressPoint.deviceId(), egressPoint.port());
-        }
+        computePorts(intent, inputPorts, outputPorts);
 
         List<Objective> objectives = new ArrayList<>();
         List<DeviceId> devices = new ArrayList<>();
@@ -112,49 +99,46 @@
                 new FlowObjectiveIntent(appId, devices, objectives, intent.resources()));
     }
 
-    private List<Objective> createRules(LinkCollectionIntent intent, DeviceId deviceId,
+    @Override
+    protected List<Objective> createRules(LinkCollectionIntent intent, DeviceId deviceId,
                                        Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
-        Set<PortNumber> ingressPorts = intent.ingressPoints().stream()
-                .filter(point -> point.deviceId().equals(deviceId))
-                .map(ConnectPoint::port)
-                .collect(Collectors.toSet());
 
-        TrafficTreatment.Builder defaultTreatmentBuilder = DefaultTrafficTreatment.builder();
-        outPorts.forEach(defaultTreatmentBuilder::setOutput);
-        TrafficTreatment defaultTreatment = defaultTreatmentBuilder.build();
+        Set<PortNumber> ingressPorts = Sets.newHashSet();
+        Set<PortNumber> egressPorts = Sets.newHashSet();
 
-        TrafficTreatment.Builder ingressTreatmentBuilder = DefaultTrafficTreatment.builder(intent.treatment());
-        outPorts.forEach(ingressTreatmentBuilder::setOutput);
-        TrafficTreatment ingressTreatment = ingressTreatmentBuilder.build();
+        computePorts(intent, deviceId, ingressPorts, egressPorts);
 
         List<Objective> objectives = new ArrayList<>(inPorts.size());
-        for (PortNumber inPort: inPorts) {
-            TrafficSelector selector = DefaultTrafficSelector.builder(intent.selector()).matchInPort(inPort).build();
-            TrafficTreatment treatment;
-            if (ingressPorts.contains(inPort)) {
-                treatment = ingressTreatment;
-            } else {
-                treatment = defaultTreatment;
-            }
+        Set<PortNumber> copyIngressPorts = ImmutableSet.copyOf(ingressPorts);
+        Set<PortNumber> copyEgressPorts = ImmutableSet.copyOf(egressPorts);
+
+        inPorts.forEach(inport -> {
+            ForwardingInstructions instructions = this.createForwardingInstructions(intent,
+                                                                                    inport,
+                                                                                    outPorts,
+                                                                                    copyIngressPorts,
+                                                                                    copyEgressPorts);
 
             NextObjective nextObjective = DefaultNextObjective.builder()
                     .withId(flowObjectiveService.allocateNextId())
-                    .addTreatment(treatment)
+                    .addTreatment(instructions.treatment())
                     .withType(NextObjective.Type.SIMPLE)
                     .fromApp(appId)
                     .makePermanent().add();
             objectives.add(nextObjective);
 
             objectives.add(DefaultForwardingObjective.builder()
-                    .withSelector(selector)
+                    .withSelector(instructions.selector())
                     .nextStep(nextObjective.id())
                     .withPriority(intent.priority())
                     .fromApp(appId)
                     .makePermanent()
                     .withFlag(ForwardingObjective.Flag.SPECIFIC)
                     .add());
-        }
+            }
+        );
 
         return objectives;
     }
+
 }