@Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;

    result = prime * (int) (xid ^ (xid >>> 32));
    result = prime * result + ((tableId == null) ? 0 : tableId.hashCode());
    result = prime * result + ((checksum == null) ? 0 : checksum.hashCode());
    result = prime * result + ((key == null) ? 0 : key.hashCode());
    result = prime * result + ((value == null) ? 0 : value.hashCode());
    return result;
  }
  @Test
  public void testWrite() {
    OFBsnGentableEntryDelete.Builder builder = factory.buildBsnGentableEntryDelete();
    builder
        .setXid(0x12345678)
        .setTableId(GenTableId.of(20))
        .setKey(
            ImmutableList.<OFBsnTlv>of(
                factory.bsnTlvs().port(OFPort.of(5)),
                factory.bsnTlvs().mac(MacAddress.of("01:23:45:67:89:ab"))));
    OFBsnGentableEntryDelete bsnGentableEntryDelete = builder.build();
    ByteBuf bb = Unpooled.buffer();
    bsnGentableEntryDelete.writeTo(bb);
    byte[] written = new byte[bb.readableBytes()];
    bb.readBytes(written);

    assertThat(written, CoreMatchers.equalTo(BSN_GENTABLE_ENTRY_DELETE_SERIALIZED));
  }
    @Override
    public OFBsnGentableEntryAdd readFrom(ChannelBuffer bb) throws OFParseError {
      int start = bb.readerIndex();
      // fixed value property version == 4
      byte version = bb.readByte();
      if (version != (byte) 0x4)
        throw new OFParseError("Wrong version: Expected=OFVersion.OF_13(4), got=" + version);
      // fixed value property type == 4
      byte type = bb.readByte();
      if (type != (byte) 0x4)
        throw new OFParseError("Wrong type: Expected=OFType.EXPERIMENTER(4), got=" + type);
      int length = U16.f(bb.readShort());
      if (length < MINIMUM_LENGTH)
        throw new OFParseError(
            "Wrong length: Expected to be >= " + MINIMUM_LENGTH + ", was: " + length);
      if (bb.readableBytes() + (bb.readerIndex() - start) < length) {
        // Buffer does not have all data yet
        bb.readerIndex(start);
        return null;
      }
      if (logger.isTraceEnabled()) logger.trace("readFrom - length={}", length);
      long xid = U32.f(bb.readInt());
      // fixed value property experimenter == 0x5c16c7L
      int experimenter = bb.readInt();
      if (experimenter != 0x5c16c7)
        throw new OFParseError(
            "Wrong experimenter: Expected=0x5c16c7L(0x5c16c7L), got=" + experimenter);
      // fixed value property subtype == 0x2eL
      int subtype = bb.readInt();
      if (subtype != 0x2e)
        throw new OFParseError("Wrong subtype: Expected=0x2eL(0x2eL), got=" + subtype);
      GenTableId tableId = GenTableId.read2Bytes(bb);
      int keyLength = U16.f(bb.readShort());
      U128 checksum = U128.read16Bytes(bb);
      List<OFBsnTlv> key = ChannelUtils.readList(bb, keyLength, OFBsnTlvVer13.READER);
      List<OFBsnTlv> value =
          ChannelUtils.readList(bb, length - (bb.readerIndex() - start), OFBsnTlvVer13.READER);

      OFBsnGentableEntryAddVer13 bsnGentableEntryAddVer13 =
          new OFBsnGentableEntryAddVer13(xid, tableId, checksum, key, value);
      if (logger.isTraceEnabled()) logger.trace("readFrom - read={}", bsnGentableEntryAddVer13);
      return bsnGentableEntryAddVer13;
    }
  @Test
  public void testRead() throws Exception {
    OFBsnGentableEntryDelete.Builder builder = factory.buildBsnGentableEntryDelete();
    builder
        .setXid(0x12345678)
        .setTableId(GenTableId.of(20))
        .setKey(
            ImmutableList.<OFBsnTlv>of(
                factory.bsnTlvs().port(OFPort.of(5)),
                factory.bsnTlvs().mac(MacAddress.of("01:23:45:67:89:ab"))));
    OFBsnGentableEntryDelete bsnGentableEntryDeleteBuilt = builder.build();

    ByteBuf input = Unpooled.copiedBuffer(BSN_GENTABLE_ENTRY_DELETE_SERIALIZED);

    // FIXME should invoke the overall reader once implemented
    OFBsnGentableEntryDelete bsnGentableEntryDeleteRead =
        OFBsnGentableEntryDeleteVer13.READER.readFrom(input);
    assertEquals(BSN_GENTABLE_ENTRY_DELETE_SERIALIZED.length, input.readerIndex());

    assertEquals(bsnGentableEntryDeleteBuilt, bsnGentableEntryDeleteRead);
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    OFBsnGentableEntryAddVer13 other = (OFBsnGentableEntryAddVer13) obj;

    if (xid != other.xid) return false;
    if (tableId == null) {
      if (other.tableId != null) return false;
    } else if (!tableId.equals(other.tableId)) return false;
    if (checksum == null) {
      if (other.checksum != null) return false;
    } else if (!checksum.equals(other.checksum)) return false;
    if (key == null) {
      if (other.key != null) return false;
    } else if (!key.equals(other.key)) return false;
    if (value == null) {
      if (other.value != null) return false;
    } else if (!value.equals(other.value)) return false;
    return true;
  }