class EthernetDecoder extends AbstractPacketDecoder<PacketReceived, EthernetPacketReceived> implements PacketProcessingListener {

    @Override
    public EthernetPacketReceived decode(PacketReceived packetReceived) {
        byte[] data = packetReceived.getPayload();
        EthernetPacketReceivedBuilder builder = new EthernetPacketReceivedBuilder();

        // Save original rawPacket & set the payloadOffset/payloadLength fields
        RawPacketBuilder rpb = new RawPacketBuilder().setIngress(packetReceived.getIngress())
                .setConnectionCookie(packetReceived.getConnectionCookie()).setFlowCookie(packetReceived.getFlowCookie())
                .setTableId(packetReceived.getTableId()).setPacketInReason(packetReceived.getPacketInReason())
                .setPayloadOffset(0).setPayloadLength(data.length);
        if (packetReceived.getMatch() != null) {
            rpb.setMatch(new MatchBuilder(packetReceived.getMatch()).build());
        }
        RawPacket rp = rpb.build();
        ArrayList<PacketChain> packetChain = new ArrayList<>();
        packetChain.add(new PacketChainBuilder().setPacket(rp).build());

        try {
            EthernetPacketBuilder epBuilder = new EthernetPacketBuilder();

            // Deserialize the destination & source fields
            epBuilder.setDestinationMac(
                    new MacAddress(HexEncode.bytesToHexStringFormat(BitBufferHelper.getBits(data, 0, 48))));
            epBuilder.setSourceMac(
                    new MacAddress(HexEncode.bytesToHexStringFormat(BitBufferHelper.getBits(data, 48, 48))));

            // Deserialize the optional field 802.1Q headers
            Integer nextField = BitBufferHelper.getInt(BitBufferHelper.getBits(data, 96, 16));
            int extraHeaderBits = 0;
            ArrayList<Header8021q> headerList = new ArrayList<>();
            while (nextField.equals(ETHERTYPE_8021Q) || nextField.equals(ETHERTYPE_QINQ)) {
                Header8021qBuilder headerBuilder = new Header8021qBuilder();
                headerBuilder.setTPID(Header8021qType.forValue(nextField));

                // Read 2 more bytes for priority (3bits), drop eligible (1bit),
                // vlan-id (12bits)
                byte[] vlanBytes = BitBufferHelper.getBits(data, 112 + extraHeaderBits, 16);

                // Remove the sign & right-shift to get the priority code
                headerBuilder.setPriorityCode((short) ((vlanBytes[0] & 0xff) >> 5));

                // Remove the sign & remove priority code bits & right-shift to
                // get drop-eligible bit
                headerBuilder.setDropEligible(1 == (vlanBytes[0] & 0xff & 0x10) >> 4);

                // Remove priority code & drop-eligible bits, to get the VLAN-id
                vlanBytes[0] = (byte) (vlanBytes[0] & 0x0F);
                headerBuilder.setVlan(new VlanId(BitBufferHelper.getInt(vlanBytes)));

                // Add 802.1Q header to the growing collection
                headerList.add(headerBuilder.build());

                // Reset value of "nextField" to correspond to following 2 bytes
                // for next 802.1Q header or EtherType/Length
                nextField = BitBufferHelper.getInt(BitBufferHelper.getBits(data, 128 + extraHeaderBits, 16));

                // 802.1Q header means payload starts at a later position
                extraHeaderBits += 32;
            }
            // Set 802.1Q headers
            if (!headerList.isEmpty()) {
                epBuilder.setHeader8021q(headerList);
            }

            // Deserialize the EtherType or Length field
            if (nextField >= ETHERTYPE_MIN) {
                epBuilder.setEthertype(KnownEtherType.forValue(nextField));
            } else if (nextField <= LENGTH_MAX) {
                epBuilder.setEthernetLength(nextField);
            } else {
                LOG.debug("Undefined header, value is not valid EtherType or length.  Value is {}", nextField);
            }

            // Determine start & end of payload
            int payloadStart = (112 + extraHeaderBits) / NetUtils.NUM_BITS_IN_A_BYTE;
            int payloadEnd = data.length - 4;
            epBuilder.setPayloadOffset(payloadStart);
            epBuilder.setPayloadLength(payloadEnd - payloadStart);

            // Deserialize the CRC
            epBuilder.setCrc(BitBufferHelper
                    .getLong(BitBufferHelper.getBits(data, (data.length - 4) * NetUtils.NUM_BITS_IN_A_BYTE, 32)));

            // Set EthernetPacket field
            packetChain.add(new PacketChainBuilder().setPacket(epBuilder.build()).build());

            // Set Payload field
            builder.setPayload(data);
        } catch (BufferException be) {
            LOG.info("Exception during decoding raw packet to ethernet.");
        }

        // ToDo: Possibly log these values
        /*
         * if (_logger.isTraceEnabled()) {
         * _logger.trace("{}: {}: {} (offset {} bitsize {})", new Object[] {
         * this.getClass().getSimpleName(), hdrField,
         * HexEncode.bytesToHexString(hdrFieldBytes), startOffset, numBits }); }
         */
        builder.setPacketChain(packetChain);
        return builder.build();
    }

    public  EthernetDecoder(NotificationProviderService notificationProviderService);

}