[ONOS-5750] Add MappingEntryBuilder with unit test

Change-Id: Ia4e2f4b6cb3a9b07a00376b12b506f43255bad11
diff --git a/providers/lisp/mapping/src/main/java/org/onosproject/provider/lisp/mapping/util/MappingEntryBuilder.java b/providers/lisp/mapping/src/main/java/org/onosproject/provider/lisp/mapping/util/MappingEntryBuilder.java
index 12a58e4..90052c28 100644
--- a/providers/lisp/mapping/src/main/java/org/onosproject/provider/lisp/mapping/util/MappingEntryBuilder.java
+++ b/providers/lisp/mapping/src/main/java/org/onosproject/provider/lisp/mapping/util/MappingEntryBuilder.java
@@ -15,49 +15,246 @@
  */
 package org.onosproject.provider.lisp.mapping.util;
 
-import org.onosproject.lisp.msg.protocols.LispMapNotify;
-import org.onosproject.lisp.msg.protocols.LispMapReply;
+import com.google.common.collect.Lists;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.lisp.msg.protocols.LispLocator;
+import org.onosproject.lisp.msg.protocols.LispMapRecord;
+import org.onosproject.lisp.msg.types.LispAfiAddress;
+import org.onosproject.lisp.msg.types.LispAsAddress;
+import org.onosproject.lisp.msg.types.LispDistinguishedNameAddress;
+import org.onosproject.lisp.msg.types.LispIpv4Address;
+import org.onosproject.lisp.msg.types.LispIpv6Address;
+import org.onosproject.lisp.msg.types.LispMacAddress;
+import org.onosproject.mapping.DefaultMapping;
+import org.onosproject.mapping.DefaultMappingEntry;
+import org.onosproject.mapping.DefaultMappingKey;
+import org.onosproject.mapping.DefaultMappingTreatment;
+import org.onosproject.mapping.DefaultMappingValue;
+import org.onosproject.mapping.Mapping;
 import org.onosproject.mapping.MappingEntry;
+import org.onosproject.mapping.MappingEntry.MappingEntryState;
+import org.onosproject.mapping.MappingKey;
+import org.onosproject.mapping.MappingTreatment;
+import org.onosproject.mapping.MappingValue;
+import org.onosproject.mapping.actions.MappingAction;
+import org.onosproject.mapping.actions.MappingActions;
+import org.onosproject.mapping.addresses.MappingAddress;
+import org.onosproject.mapping.addresses.MappingAddresses;
 import org.onosproject.net.DeviceId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
+import java.util.UUID;
+
+import static org.onosproject.lisp.msg.types.AddressFamilyIdentifierEnum.IP4;
+import static org.onosproject.lisp.msg.types.AddressFamilyIdentifierEnum.IP6;
+
 /**
  * Mapping entry builder class.
  */
 public class MappingEntryBuilder {
-    private static final Logger log = LoggerFactory.getLogger(MappingEntryBuilder.class);
+    private static final Logger log =
+                            LoggerFactory.getLogger(MappingEntryBuilder.class);
+
+    private static final int IPV4_PREFIX_LENGTH = 32;
+    private static final int IPV6_PREFIX_LENGTH = 128;
 
     private final DeviceId deviceId;
-    private final LispMapReply mapReply;
-    private final LispMapNotify mapNotify;
+
+    private final MappingAddress address;
+    private final MappingAction action;
+    private final List<MappingTreatment> treatments;
 
     /**
      * Default constructor for MappingEntryBuilder.
      *
      * @param deviceId device identifier
-     * @param mapReply map reply message
+     * @param record   LISP map record
      */
-    public MappingEntryBuilder(DeviceId deviceId, LispMapReply mapReply) {
+    public MappingEntryBuilder(DeviceId deviceId, LispMapRecord record) {
         this.deviceId = deviceId;
-        this.mapReply = mapReply;
-        this.mapNotify = null;
+        this.address = buildAddress(record);
+        this.action = buildAction(record);
+        this.treatments = buildTreatments(record);
     }
 
     /**
-     * Default constructor for MappingEntryBuilder.
+     * Builds mapping entry from a specific LISP control message.
      *
-     * @param deviceId  device identifier
-     * @param mapNotify map notify message
+     * @return mapping entry
      */
-    public MappingEntryBuilder(DeviceId deviceId, LispMapNotify mapNotify) {
-        this.deviceId = deviceId;
-        this.mapNotify = mapNotify;
-        this.mapReply = null;
+    public MappingEntry build() {
+        Mapping.Builder builder;
+
+        // we assign leastSignificantBits of UUID as the mapping identifier for now
+        // id generation scheme can be changed later
+        UUID uuid = UUID.randomUUID();
+
+        builder = DefaultMapping.builder()
+                .withId(uuid.getLeastSignificantBits())
+                .forDevice(deviceId)
+                .withKey(buildKey())
+                .withValue(buildValue());
+
+        // TODO: we assume that the mapping entry will be always
+        // stored in routers without failure for now, which means
+        // the mapping entry state will always be ADDED rather than
+        // PENDING_ADD
+        // we will revisit this part when LISP driver is finished
+        return new DefaultMappingEntry(builder.build(), MappingEntryState.ADDED);
     }
 
-    public MappingEntry build() {
-        // TODO: provide a way to build mapping entry from input parameters
+    /**
+     * Builds mapping key.
+     *
+     * @return mapping key
+     */
+    private MappingKey buildKey() {
+
+        MappingKey.Builder builder = DefaultMappingKey.builder();
+
+        builder.withAddress(address);
+
+        return builder.build();
+    }
+
+    /**
+     * Builds mapping value.
+     *
+     * @return mapping value
+     */
+    private MappingValue buildValue() {
+
+        MappingValue.Builder builder = DefaultMappingValue.builder();
+        builder.withAction(action);
+
+        treatments.forEach(builder::add);
+
+        return builder.build();
+    }
+
+    /**
+     * Builds mapping action.
+     *
+     * @param record LISP map record
+     * @return mapping action
+     */
+    private MappingAction buildAction(LispMapRecord record) {
+
+        if (record == null) {
+            return MappingActions.noAction();
+        }
+
+        switch (record.getAction()) {
+            case NoAction:
+                return MappingActions.noAction();
+            case SendMapRequest:
+                return MappingActions.forward();
+            case NativelyForward:
+                return MappingActions.nativeForward();
+            case Drop:
+                return MappingActions.drop();
+            default:
+                log.warn("Unsupported action type {}", record.getAction());
+                return MappingActions.noAction();
+        }
+    }
+
+    /**
+     * Builds mapping address.
+     *
+     * @param record LISP map record
+     * @return mapping address
+     */
+    private MappingAddress buildAddress(LispMapRecord record) {
+
+        return record == null ? null :
+                getAddress(record.getEidPrefixAfi(), record.getMaskLength());
+    }
+
+    /**
+     * Converts LispAfiAddress into abstracted mapping address.
+     *
+     * @param address LispAfiAddress
+     * @param length  mask length
+     * @return abstracted mapping address
+     */
+    private MappingAddress getAddress(LispAfiAddress address, int length) {
+
+        if (address == null) {
+            log.warn("Address is not specified.");
+            return null;
+        }
+
+        switch (address.getAfi()) {
+            case IP4:
+                IpAddress ipv4Address = ((LispIpv4Address) address).getAddress();
+                IpPrefix ipv4Prefix = IpPrefix.valueOf(ipv4Address, length);
+                return MappingAddresses.ipv4MappingAddress(ipv4Prefix);
+            case IP6:
+                IpAddress ipv6Address = ((LispIpv6Address) address).getAddress();
+                IpPrefix ipv6Prefix = IpPrefix.valueOf(ipv6Address, length);
+                return MappingAddresses.ipv6MappingAddress(ipv6Prefix);
+            case AS:
+                int asNum = ((LispAsAddress) address).getASNum();
+                return MappingAddresses.asMappingAddress(String.valueOf(asNum));
+            case DISTINGUISHED_NAME:
+                String dn = ((LispDistinguishedNameAddress)
+                                                address).getDistinguishedName();
+                return MappingAddresses.dnMappingAddress(dn);
+            case MAC:
+                MacAddress macAddress = ((LispMacAddress) address).getAddress();
+                return MappingAddresses.ethMappingAddress(macAddress);
+            case LCAF:
+                // TODO: need to use extension address to abstract LCAF address
+                break;
+            default:
+                log.warn("Unsupported address type {}", address.getAfi());
+                break;
+        }
+
         return null;
     }
+
+    /**
+     * Builds a collection of mapping treatments.
+     *
+     * @param record LISP map record
+     * @return a collection of mapping treatments
+     */
+    private List<MappingTreatment> buildTreatments(LispMapRecord record) {
+
+        List<LispLocator> locators = record.getLocators();
+        List<MappingTreatment> treatments = Lists.newArrayList();
+        for (LispLocator locator : locators) {
+            MappingTreatment.Builder builder = DefaultMappingTreatment.builder();
+            LispAfiAddress address = locator.getLocatorAfi();
+            int addressLength = 0;
+            if (address.getAfi() == IP4) {
+                addressLength = IPV4_PREFIX_LENGTH;
+            } else if (address.getAfi() == IP6) {
+                addressLength = IPV6_PREFIX_LENGTH;
+            }
+
+            final MappingAddress mappingAddress = getAddress(address, addressLength);
+            if (mappingAddress != null) {
+                builder.withAddress(mappingAddress);
+            }
+
+            builder.setUnicastWeight(locator.getWeight())
+                    .setUnicastPriority(locator.getPriority())
+                    .setMulticastWeight(locator.getMulticastWeight())
+                    .setMulticastPriority(locator.getMulticastPriority());
+
+            // TODO: need to convert specific properties to
+            // abstracted extension properties
+
+            treatments.add(builder.build());
+        }
+
+        return treatments;
+    }
 }