Bmp header message deserializer

Change-Id: I81b6ddc2db8a154c048240005dda0d11ea77f47f
diff --git a/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/BmpHeader.java b/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/BmpHeader.java
new file mode 100644
index 0000000..5405ae2
--- /dev/null
+++ b/apps/bgpmonitoring/app/src/main/java/org/onosproject/bgpmonitoring/type/BmpHeader.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2024-present Open Networking Foundation
+ *
+ * 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.bgpmonitoring.type;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+
+import java.nio.ByteBuffer;
+import java.util.function.BiPredicate;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.onosproject.bgpmonitoring.BmpMessage;
+import org.onosproject.bgpmonitoring.BmpVersion;
+import org.onosproject.bgpmonitoring.BmpPacket;
+import org.onosproject.bgpmonitoring.BmpParseException;
+
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * The following common header appears in all BMP messages.  The rest of
+ * the data in a BMP message is dependent on the Message Type field in
+ * the common header.
+ * <p>
+ * Version (1 byte): Indicates the BMP version.  This is set to '3'
+ * for all messages defined in this specification. ('1' and '2' were
+ * used by draft versions of this document.)  Version 0 is reserved
+ * and MUST NOT be sent.
+ * <p>
+ * Message Length (4 bytes): Length of the message in bytes
+ * (including headers, data, and encapsulated messages, if any).
+ * <p>
+ * Message Type (1 byte): This identifies the type of the BMP
+ * message.  A BMP implementation MUST ignore unrecognized message
+ * types upon receipt.
+ * <p>
+ * Type = 0: Route Monitoring
+ * Type = 1: Statistics Report
+ * Type = 2: Peer Down Notification
+ * Type = 3: Peer Up Notification
+ * Type = 4: Initiation Message
+ * Type = 5: Termination Message
+ * Type = 6: Route Mirroring Message
+ */
+public final class BmpHeader extends BmpPacket {
+
+    /*
+
+      0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-+-+-+-+
+     |    Version    |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                        Message Length                         |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |   Msg. Type   |
+     +---------------+
+
+     */
+
+
+    public static final int DEFAULT_HEADER_LENGTH = 6;
+    public static final int DEFAULT_PACKET_MINIMUM_LENGTH = 4;
+
+    private BmpVersion version;
+
+    private BmpType type;
+
+    private int length;
+
+    private BmpMessage message;
+
+
+    public enum BmpType {
+
+        ROUTE_MONITORING(0, BmpRouteMirroring.deserializer()),
+
+        STATISTICS_REPORT(1, BmpStatsReport.deserializer()),
+
+        PEER_DOWN_NOTIFICATION(2, BmpPeerDownNotification.deserializer()),
+
+        PEER_UP_NOTIFICATION(3, BmpPeerUpNotification.deserializer()),
+
+        INITIATION_MESSAGE(4, BmpInitiationMessage.deserializer()),
+
+        TERMINATION_MESSAGE(5, BmpTerminationMessage.deserializer()),
+
+        ROUTE_MIRRORING_MESSAGE(6, BmpRouteMirroring.deserializer());
+
+        private final int value;
+        private final Deserializer<BmpMessage> deserializer;
+
+        /**
+         * Assign value with the value val as the types of BMP message.
+         *
+         * @param val          type of BMP message
+         * @param deserializer deserializer
+         */
+        BmpType(int val, Deserializer deserializer) {
+            this.value = val;
+            this.deserializer = deserializer;
+        }
+
+
+        /**
+         * Returns value as type of BMP message.
+         *
+         * @return value type of BMP message
+         */
+        public int getType() {
+            return value;
+        }
+
+        private static Map<Integer, BmpType> parser = new ConcurrentHashMap<>();
+
+        static {
+            Arrays.stream(BmpType.values()).forEach(type -> parser.put(type.value, type));
+        }
+
+        public static BmpType getType(int type) throws DeserializationException {
+            if (type > 6) {
+                throw new DeserializationException("Invalid trap type");
+            }
+            return Optional.of(type)
+                    .filter(id -> parser.containsKey(id))
+                    .map(id -> parser.get(id))
+                    .orElse(ROUTE_MONITORING);
+        }
+
+        public Deserializer<BmpMessage> getDecoder() {
+            return this.deserializer;
+        }
+
+    }
+
+
+    private BmpHeader(Builder builder) {
+        this.version = builder.version;
+        this.length = builder.length;
+        this.type = builder.type;
+        this.message = builder.message;
+    }
+
+
+    /**
+     * Returns message length.
+     *
+     * @return message length
+     */
+    @Override
+    public int getLength() {
+        return this.length;
+    }
+
+    /**
+     * Returns message version.
+     *
+     * @return message version
+     */
+    @Override
+    public BmpVersion getVersion() {
+        return this.version;
+    }
+
+    /**
+     * Returns message type.
+     *
+     * @return message type
+     */
+    @Override
+    public String getType() {
+        return this.type.name();
+    }
+
+    /**
+     * Returns message type.
+     *
+     * @return message type
+     */
+    @Override
+    public BmpMessage getMessage() {
+        return this.message;
+    }
+
+
+    /**
+     * Read from channel buffer and Returns BMP header.
+     *
+     * @return Deserializer Deserializer
+     */
+    public static Deserializer<BmpHeader> deserializer() {
+        return (data, offset, length) -> {
+            BiPredicate<ByteBuffer, Integer> isValidBuffer = (b, l)
+                    -> b.hasRemaining() && b.remaining() >= l;
+
+            ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+            if (!isValidBuffer.test(bb, DEFAULT_HEADER_LENGTH)) {
+                throw new BmpParseException("Invalid bmp header buffer size.");
+            }
+            Builder builder = new Builder()
+                    .version(BmpVersion.getVersion((int) bb.get()))
+                    .length(bb.getInt())
+                    .type(BmpType.getType((int) bb.get()));
+
+            if (bb.remaining() != (builder.length - DEFAULT_HEADER_LENGTH)) {
+                throw new BmpParseException("Invalid bmp header buffer size.");
+            }
+
+            byte[] msgBytes = new byte[bb.remaining()];
+            bb.get(msgBytes);
+            return builder.message(builder.type.getDecoder()
+                            .deserialize(msgBytes, 0, (builder.length - DEFAULT_HEADER_LENGTH)))
+                    .build();
+
+        };
+    }
+
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("version", version)
+                .add("type", type)
+                .add("length", length)
+                .toString();
+    }
+
+    /**
+     * Builder for BMP header.
+     */
+    private static class Builder {
+
+        private BmpVersion version;
+
+        private BmpType type;
+
+        private int length;
+
+        private BmpMessage message;
+
+
+        /**
+         * Setter bmp version.
+         *
+         * @param version bmp version.
+         * @return this class builder.
+         */
+        public Builder version(BmpVersion version) {
+            this.version = version;
+            return this;
+        }
+
+
+        /**
+         * Setter bmp header length.
+         *
+         * @param length bmp header length.
+         * @return this class builder.
+         */
+        public Builder length(int length) {
+            this.length = length;
+            return this;
+        }
+
+        /**
+         * Setter bmp message type.
+         *
+         * @param type bmp message type.
+         * @return this class builder.
+         */
+        public Builder type(BmpType type) {
+            this.type = type;
+            return this;
+        }
+
+
+        /**
+         * Setter bmp message.
+         *
+         * @param message bmp message.
+         * @return this class builder.
+         */
+        public Builder message(BmpMessage message) {
+            this.message = message;
+            return this;
+        }
+
+
+        /**
+         * Checks arguments for bmp header.
+         */
+        private void checkArguments() {
+            checkState(length != 0, "Invalid bmp header length.");
+            checkState(type != null, "Invalid bmp message type.");
+            checkState(message != null, "Invalid bmp message.");
+        }
+
+        /**
+         * Builds BMP header.
+         *
+         * @return BMP header.
+         */
+        public BmpHeader build() {
+            checkArguments();
+            return new BmpHeader(this);
+        }
+    }
+}