[ONOS-6142] Add SrcDst and TE extension addresses with unit tests

Change-Id: If16b1217d44774e91e0452aedb53daacf33f4e23
diff --git a/drivers/lisp/src/main/java/org/onosproject/drivers/lisp/extensions/LispSrcDstAddress.java b/drivers/lisp/src/main/java/org/onosproject/drivers/lisp/extensions/LispSrcDstAddress.java
index 1c1dee7..95c3be3 100644
--- a/drivers/lisp/src/main/java/org/onosproject/drivers/lisp/extensions/LispSrcDstAddress.java
+++ b/drivers/lisp/src/main/java/org/onosproject/drivers/lisp/extensions/LispSrcDstAddress.java
@@ -16,10 +16,16 @@
 
 package org.onosproject.drivers.lisp.extensions;
 
+import com.google.common.collect.Maps;
 import org.onosproject.mapping.addresses.ExtensionMappingAddress;
 import org.onosproject.mapping.addresses.ExtensionMappingAddressType;
+import org.onosproject.mapping.addresses.MappingAddress;
 import org.onosproject.net.flow.AbstractExtension;
 
+import java.util.Map;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
 import static org.onosproject.mapping.addresses.ExtensionMappingAddressType
                             .ExtensionMappingAddressTypes.SOURCE_DEST_ADDRESS;
 
@@ -31,6 +37,75 @@
  */
 public class LispSrcDstAddress extends AbstractExtension
                                             implements ExtensionMappingAddress {
+
+    private static final String SRC_MASK_LENGTH = "srcMaskLength";
+    private static final String DST_MASK_LENGTH = "dstMaskLength";
+    private static final String SRC_PREFIX = "srcPrefix";
+    private static final String DST_PREFIX = "dstPrefix";
+
+    private byte srcMaskLength;
+    private byte dstMaskLength;
+    private MappingAddress srcPrefix;
+    private MappingAddress dstPrefix;
+
+    /**
+     * Default constructor.
+     */
+    public LispSrcDstAddress() {
+    }
+
+    /**
+     * Creates an instance with initialized parameters.
+     *
+     * @param srcMaskLength source mask length
+     * @param dstMaskLength destination mask length
+     * @param srcPrefix     source address prefix
+     * @param dstPrefix     destination address prefix
+     */
+    private LispSrcDstAddress(byte srcMaskLength, byte dstMaskLength,
+                              MappingAddress srcPrefix, MappingAddress dstPrefix) {
+        this.srcMaskLength = srcMaskLength;
+        this.dstMaskLength = dstMaskLength;
+        this.srcPrefix = srcPrefix;
+        this.dstPrefix = dstPrefix;
+    }
+
+    /**
+     * Obtains source mask length.
+     *
+     * @return source mask length
+     */
+    public byte getSrcMaskLength() {
+        return srcMaskLength;
+    }
+
+    /**
+     * Obtains destination mask length.
+     *
+     * @return destination mask length
+     */
+    public byte getDstMaskLength() {
+        return dstMaskLength;
+    }
+
+    /**
+     * Obtains source address prefix.
+     *
+     * @return source address prefix
+     */
+    public MappingAddress getSrcPrefix() {
+        return srcPrefix;
+    }
+
+    /**
+     * Obtains destination address prefix.
+     *
+     * @return destination address prefix
+     */
+    public MappingAddress getDstPrefix() {
+        return dstPrefix;
+    }
+
     @Override
     public ExtensionMappingAddressType type() {
         return SOURCE_DEST_ADDRESS.type();
@@ -38,11 +113,119 @@
 
     @Override
     public byte[] serialize() {
-        return new byte[0];
+        Map<String, Object> parameterMap = Maps.newHashMap();
+
+        parameterMap.put(SRC_MASK_LENGTH, srcMaskLength);
+        parameterMap.put(DST_MASK_LENGTH, dstMaskLength);
+        parameterMap.put(SRC_PREFIX, srcPrefix);
+        parameterMap.put(DST_PREFIX, dstPrefix);
+
+        return APP_KRYO.serialize(parameterMap);
     }
 
     @Override
     public void deserialize(byte[] data) {
+        Map<String, Object> parameterMap = APP_KRYO.deserialize(data);
 
+        this.srcMaskLength = (byte) parameterMap.get(SRC_MASK_LENGTH);
+        this.dstMaskLength = (byte) parameterMap.get(DST_MASK_LENGTH);
+        this.srcPrefix = (MappingAddress) parameterMap.get(SRC_PREFIX);
+        this.dstPrefix = (MappingAddress) parameterMap.get(DST_PREFIX);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(srcPrefix, dstPrefix, srcMaskLength, dstMaskLength);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof LispSrcDstAddress) {
+            final LispSrcDstAddress other = (LispSrcDstAddress) obj;
+            return Objects.equals(this.srcPrefix, other.srcPrefix) &&
+                    Objects.equals(this.dstPrefix, other.dstPrefix) &&
+                    Objects.equals(this.srcMaskLength, other.srcMaskLength) &&
+                    Objects.equals(this.dstMaskLength, other.dstMaskLength);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("source prefix", srcPrefix)
+                .add("destination prefix", dstPrefix)
+                .add("source mask length", srcMaskLength)
+                .add("destination mask length", dstMaskLength)
+                .toString();
+    }
+
+    /**
+     * A builder for building LispSrcDstAddress.
+     */
+    public static final class Builder {
+        private byte srcMaskLength;
+        private byte dstMaskLength;
+        private MappingAddress srcPrefix;
+        private MappingAddress dstPrefix;
+
+        /**
+         * Sets source address prefix.
+         *
+         * @param srcPrefix source prefix
+         * @return Builder object
+         */
+        public Builder withSrcPrefix(MappingAddress srcPrefix) {
+            this.srcPrefix = srcPrefix;
+            return this;
+        }
+
+        /**
+         * Sets destination address prefix.
+         *
+         * @param dstPrefix destination prefix
+         * @return Builder object
+         */
+        public Builder withDstPrefix(MappingAddress dstPrefix) {
+            this.dstPrefix = dstPrefix;
+            return this;
+        }
+
+        /**
+         * Sets source mask length.
+         *
+         * @param srcMaskLength source mask length
+         * @return Builder object
+         */
+        public Builder withSrcMaskLength(byte srcMaskLength) {
+            this.srcMaskLength = srcMaskLength;
+            return this;
+        }
+
+        /**
+         * Sets destination mask length.
+         *
+         * @param dstMaskLength destination mask length
+         * @return Builder object
+         */
+        public Builder withDstMaskLength(byte dstMaskLength) {
+            this.dstMaskLength = dstMaskLength;
+            return this;
+        }
+
+        /**
+         * Builds LispSrcDstAddress instance.
+         *
+         * @return LispSrcDstAddress instance
+         */
+        public LispSrcDstAddress build() {
+
+            return new LispSrcDstAddress(srcMaskLength, dstMaskLength,
+                                                        srcPrefix, dstPrefix);
+        }
     }
 }
diff --git a/drivers/lisp/src/main/java/org/onosproject/drivers/lisp/extensions/LispTeAddress.java b/drivers/lisp/src/main/java/org/onosproject/drivers/lisp/extensions/LispTeAddress.java
index 4415d30..c569e61 100644
--- a/drivers/lisp/src/main/java/org/onosproject/drivers/lisp/extensions/LispTeAddress.java
+++ b/drivers/lisp/src/main/java/org/onosproject/drivers/lisp/extensions/LispTeAddress.java
@@ -16,10 +16,24 @@
 
 package org.onosproject.drivers.lisp.extensions;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.mapping.addresses.ASMappingAddress;
+import org.onosproject.mapping.addresses.DNMappingAddress;
+import org.onosproject.mapping.addresses.EthMappingAddress;
 import org.onosproject.mapping.addresses.ExtensionMappingAddress;
 import org.onosproject.mapping.addresses.ExtensionMappingAddressType;
+import org.onosproject.mapping.addresses.IPMappingAddress;
+import org.onosproject.mapping.addresses.MappingAddress;
 import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.store.serializers.KryoNamespaces;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
 import static org.onosproject.mapping.addresses.ExtensionMappingAddressType
                     .ExtensionMappingAddressTypes.TRAFFIC_ENGINEERING_ADDRESS;
 
@@ -30,6 +44,46 @@
  */
 public final class LispTeAddress extends AbstractExtension
                                             implements ExtensionMappingAddress {
+
+    private static final String TE_RECORDS = "records";
+
+    private List<TeRecord> records;
+
+    private KryoNamespace appKryo = new KryoNamespace.Builder()
+                                            .register(KryoNamespaces.API)
+                                            .register(MappingAddress.class)
+                                            .register(MappingAddress.Type.class)
+                                            .register(IPMappingAddress.class)
+                                            .register(ASMappingAddress.class)
+                                            .register(DNMappingAddress.class)
+                                            .register(EthMappingAddress.class)
+                                            .register(TeRecord.class)
+                                            .build();
+
+    /**
+     * Default constructor.
+     */
+    public LispTeAddress() {
+    }
+
+    /**
+     * Creates an instance with initialized parameters.
+     *
+     * @param records a collection of TE records
+     */
+    private LispTeAddress(List<TeRecord> records) {
+        this.records = records;
+    }
+
+    /**
+     * Obtains a collection of TE records.
+     *
+     * @return a collection of TE records
+     */
+    public List<TeRecord> getTeRecords() {
+        return ImmutableList.copyOf(records);
+    }
+
     @Override
     public ExtensionMappingAddressType type() {
         return TRAFFIC_ENGINEERING_ADDRESS.type();
@@ -37,11 +91,224 @@
 
     @Override
     public byte[] serialize() {
-        return new byte[0];
+        Map<String, Object> parameterMap = Maps.newHashMap();
+
+        parameterMap.put(TE_RECORDS, records);
+        return appKryo.serialize(parameterMap);
     }
 
     @Override
     public void deserialize(byte[] data) {
+        Map<String, Object> parameterMap = appKryo.deserialize(data);
 
+        this.records = (List<TeRecord>) parameterMap.get(TE_RECORDS);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(records);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof LispTeAddress) {
+            final LispTeAddress other = (LispTeAddress) obj;
+            return Objects.equals(records, other.records);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("TE records", records).toString();
+    }
+
+    /**
+     * A builder for building LispTeAddress.
+     */
+    public static final class Builder {
+        private List<TeRecord> records;
+
+        /**
+         * Sets a collection of TE records.
+         *
+         * @param records a collection of TE records
+         * @return Builder object
+         */
+        public Builder withTeRecords(List<TeRecord> records) {
+            this.records = records;
+            return this;
+        }
+
+        /**
+         * Builds LispTeAddress instance.
+         *
+         * @return LispTeAddress instance
+         */
+        public LispTeAddress build() {
+            return new LispTeAddress(records);
+        }
+    }
+
+    /**
+     * Traffic engineering record class.
+     */
+    public static final class TeRecord {
+        private boolean lookup;
+        private boolean rlocProbe;
+        private boolean strict;
+        private MappingAddress address;
+
+        /**
+         *
+         *
+         * @param lookup     lookup bit
+         * @param rlocProbe  rloc probe bit
+         * @param strict     strict bit
+         * @param address    RTR address
+         */
+        private TeRecord(boolean lookup, boolean rlocProbe, boolean strict,
+                         MappingAddress address) {
+            this.lookup = lookup;
+            this.rlocProbe = rlocProbe;
+            this.strict = strict;
+            this.address = address;
+        }
+
+        /**
+         * Obtains lookup bit flag.
+         *
+         * @return lookup bit flag
+         */
+        public boolean isLookup() {
+            return lookup;
+        }
+
+        /**
+         * Obtains RLOC probe bit flag.
+         *
+         * @return RLOC probe bit flag
+         */
+        public boolean isRlocProbe() {
+            return rlocProbe;
+        }
+
+        /**
+         * Obtains strict bit flag.
+         *
+         * @return strict bit flag
+         */
+        public boolean isStrict() {
+            return strict;
+        }
+
+        /**
+         * Obtains Re-encapsulated RLOC address.
+         *
+         * @return Re-encapsulated RLOC address
+         */
+        public MappingAddress getAddress() {
+            return address;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(lookup, rlocProbe, strict, address);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+
+            if (obj instanceof TeRecord) {
+                final TeRecord other = (TeRecord) obj;
+                return Objects.equals(this.lookup, other.lookup) &&
+                        Objects.equals(this.rlocProbe, other.rlocProbe) &&
+                        Objects.equals(this.strict, other.strict) &&
+                        Objects.equals(this.address, other.address);
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(this)
+                    .add("Lookup bit", lookup)
+                    .add("RLOC probe bit", rlocProbe)
+                    .add("strict bit", strict)
+                    .add("RTR address", address)
+                    .toString();
+        }
+
+        /**
+         * A builder for building TeRecord.
+         */
+        public static final class Builder {
+            private boolean lookup;
+            private boolean rlocProbe;
+            private boolean strict;
+            private MappingAddress address;
+
+            /**
+             * Sets lookup flag.
+             *
+             * @param lookup lookup flag
+             * @return Builder object
+             */
+            public Builder withIsLookup(boolean lookup) {
+                this.lookup = lookup;
+                return this;
+            }
+
+            /**
+             * Sets RLOC probe flag.
+             *
+             * @param rlocProbe RLOC probe flag
+             * @return Builder object
+             */
+            public Builder withIsRlocProbe(boolean rlocProbe) {
+                this.rlocProbe = rlocProbe;
+                return this;
+            }
+
+            /**
+             * Sets strict flag.
+             *
+             * @param strict strict flag
+             * @return Builder object
+             */
+            public Builder withIsStrict(boolean strict) {
+                this.strict = strict;
+                return this;
+            }
+
+            /**
+             * Sets RTR RLOC address.
+             *
+             * @param address RTR RLOC address
+             * @return Builder object
+             */
+            public Builder withRtrRlocAddress(MappingAddress address) {
+                this.address = address;
+                return this;
+            }
+
+            /**
+             * Builds TeRecord instance.
+             *
+             * @return TeRcord instance
+             */
+            public TeRecord build() {
+
+                return new TeRecord(lookup, rlocProbe, strict, address);
+            }
+        }
     }
 }
diff --git a/drivers/lisp/src/test/java/org/onosproject/drivers/lisp/extensions/LispSrcDstAddressTest.java b/drivers/lisp/src/test/java/org/onosproject/drivers/lisp/extensions/LispSrcDstAddressTest.java
new file mode 100644
index 0000000..c86c672
--- /dev/null
+++ b/drivers/lisp/src/test/java/org/onosproject/drivers/lisp/extensions/LispSrcDstAddressTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017-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.drivers.lisp.extensions;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.mapping.addresses.MappingAddress;
+import org.onosproject.mapping.addresses.MappingAddresses;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Unit tests for LispSrcDstAddress extension class.
+ */
+public class LispSrcDstAddressTest {
+
+    private static final IpPrefix IP_ADDRESS_1 = IpPrefix.valueOf("1.2.3.4/24");
+    private static final IpPrefix IP_ADDRESS_2 = IpPrefix.valueOf("5.6.7.8/24");
+
+    private static final byte MASK_LENGTH_1 = 0x01;
+    private static final byte MASK_LENGTH_2 = 0x02;
+
+    private LispSrcDstAddress address1;
+    private LispSrcDstAddress sameAsAddress1;
+    private LispSrcDstAddress address2;
+
+    @Before
+    public void setUp() {
+
+        MappingAddress ma1 = MappingAddresses.ipv4MappingAddress(IP_ADDRESS_1);
+
+        address1 = new LispSrcDstAddress.Builder()
+                                    .withSrcMaskLength(MASK_LENGTH_1)
+                                    .withDstMaskLength(MASK_LENGTH_1)
+                                    .withSrcPrefix(ma1)
+                                    .withDstPrefix(ma1)
+                                    .build();
+
+        sameAsAddress1 = new LispSrcDstAddress.Builder()
+                                    .withSrcMaskLength(MASK_LENGTH_1)
+                                    .withDstMaskLength(MASK_LENGTH_1)
+                                    .withSrcPrefix(ma1)
+                                    .withDstPrefix(ma1)
+                                    .build();
+
+        MappingAddress ma2 = MappingAddresses.ipv4MappingAddress(IP_ADDRESS_2);
+
+        address2 = new LispSrcDstAddress.Builder()
+                                    .withSrcMaskLength(MASK_LENGTH_2)
+                                    .withDstMaskLength(MASK_LENGTH_2)
+                                    .withSrcPrefix(ma2)
+                                    .withDstPrefix(ma2)
+                                    .build();
+    }
+
+    @Test
+    public void testEquality() {
+        new EqualsTester()
+                .addEqualityGroup(address1, sameAsAddress1)
+                .addEqualityGroup(address2).testEquals();
+    }
+
+    @Test
+    public void testConstruction() {
+        LispSrcDstAddress address = address1;
+
+        MappingAddress ma = MappingAddresses.ipv4MappingAddress(IP_ADDRESS_1);
+
+        assertThat(address.getSrcMaskLength(), is(MASK_LENGTH_1));
+        assertThat(address.getDstMaskLength(), is(MASK_LENGTH_1));
+        assertThat(address.getSrcPrefix(), is(ma));
+        assertThat(address.getDstPrefix(), is(ma));
+    }
+
+    @Test
+    public void testSerialization() {
+        LispSrcDstAddress other = new LispSrcDstAddress();
+        other.deserialize(address1.serialize());
+
+        new EqualsTester()
+                .addEqualityGroup(address1, other)
+                .testEquals();
+    }
+}
diff --git a/drivers/lisp/src/test/java/org/onosproject/drivers/lisp/extensions/LispTeAddressTest.java b/drivers/lisp/src/test/java/org/onosproject/drivers/lisp/extensions/LispTeAddressTest.java
new file mode 100644
index 0000000..afccf87
--- /dev/null
+++ b/drivers/lisp/src/test/java/org/onosproject/drivers/lisp/extensions/LispTeAddressTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017-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.drivers.lisp.extensions;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.testing.EqualsTester;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.mapping.addresses.MappingAddress;
+import org.onosproject.mapping.addresses.MappingAddresses;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Unit tests for LispTeAddress extension class.
+ */
+public class LispTeAddressTest {
+
+    private static final IpPrefix IP_ADDRESS_1 = IpPrefix.valueOf("1.2.3.4/24");
+    private static final IpPrefix IP_ADDRESS_2 = IpPrefix.valueOf("5.6.7.8/24");
+
+    private static final boolean IS_LOOKUP_1 = false;
+    private static final boolean IS_RLOC_PROBE_1 = true;
+    private static final boolean IS_STRICT_1 = false;
+
+    private static final boolean IS_LOOKUP_2 = true;
+    private static final boolean IS_RLOC_PROBE_2 = false;
+    private static final boolean IS_STRICT_2 = true;
+
+    private LispTeAddress address1;
+    private LispTeAddress sameAsAddress1;
+    private LispTeAddress address2;
+
+    @Before
+    public void setUp() {
+
+        MappingAddress ma1 = MappingAddresses.ipv4MappingAddress(IP_ADDRESS_1);
+        MappingAddress ma2 = MappingAddresses.ipv4MappingAddress(IP_ADDRESS_2);
+
+        LispTeAddress.TeRecord tr1 = new LispTeAddress.TeRecord.Builder()
+                                                .withIsLookup(IS_LOOKUP_1)
+                                                .withIsRlocProbe(IS_RLOC_PROBE_1)
+                                                .withIsStrict(IS_STRICT_1)
+                                                .withRtrRlocAddress(ma1)
+                                                .build();
+
+        LispTeAddress.TeRecord tr2 = new LispTeAddress.TeRecord.Builder()
+                                                .withIsLookup(IS_LOOKUP_2)
+                                                .withIsRlocProbe(IS_RLOC_PROBE_2)
+                                                .withIsStrict(IS_STRICT_2)
+                                                .withRtrRlocAddress(ma2)
+                                                .build();
+
+        address1 = new LispTeAddress.Builder()
+                                        .withTeRecords(ImmutableList.of(tr1, tr2))
+                                        .build();
+
+        sameAsAddress1 = new LispTeAddress.Builder()
+                                        .withTeRecords(ImmutableList.of(tr1, tr2))
+                                        .build();
+
+        address2 = new LispTeAddress.Builder()
+                                        .withTeRecords(ImmutableList.of(tr2, tr1))
+                                        .build();
+    }
+
+    @Test
+    public void testEquality() {
+        new EqualsTester()
+                .addEqualityGroup(address1, sameAsAddress1)
+                .addEqualityGroup(address2).testEquals();
+    }
+
+    @Test
+    public void testConstruction() {
+        LispTeAddress address = address1;
+
+        MappingAddress ma = MappingAddresses.ipv4MappingAddress(IP_ADDRESS_1);
+
+        assertThat(address.getTeRecords().get(0).isLookup(), is(IS_LOOKUP_1));
+        assertThat(address.getTeRecords().get(0).isRlocProbe(), is(IS_RLOC_PROBE_1));
+        assertThat(address.getTeRecords().get(0).isStrict(), is(IS_STRICT_1));
+        assertThat(address.getTeRecords().get(0).getAddress(), is(ma));
+    }
+
+    @Test
+    public void testSerialization() {
+        LispTeAddress other = new LispTeAddress();
+        other.deserialize(address1.serialize());
+
+        new EqualsTester()
+                .addEqualityGroup(address1, other)
+                .testEquals();
+    }
+}