/** {@inheritDoc} */
  public Document encodeXML() throws InvalidLLRPMessageException {
    try {
      Namespace ns = Namespace.getNamespace("llrp", LLRPConstants.LLRPNAMESPACE);

      Element root = new Element("SET_READER_CONFIG", ns);
      //	Element root = new Element("SET_READER_CONFIG");
      root.addNamespaceDeclaration(Namespace.getNamespace("llrp", LLRPConstants.LLRPNAMESPACE));

      if (version == null) {
        throw new InvalidLLRPMessageException("Version not set");
      } else {
        root.setAttribute("Version", version.toInteger().toString());
      }

      if (messageID == null) {
        throw new InvalidLLRPMessageException("MessageID not set");
      } else {
        root.setAttribute("MessageID", messageID.toString(10));
      }

      if (resetToFactoryDefault == null) {
        LOGGER.warn(" resetToFactoryDefault not set");
        throw new MissingParameterException(" resetToFactoryDefault not set");
      } else {
        root.addContent(resetToFactoryDefault.encodeXML("ResetToFactoryDefault", ns));
      }

      // root.addContent(reserved0.encodeXML("reserved",ns));
      // parameters
      if (readerEventNotificationSpec == null) {
        LOGGER.info("readerEventNotificationSpec not set");
      } else {
        root.addContent(
            readerEventNotificationSpec.encodeXML(
                readerEventNotificationSpec.getClass().getSimpleName(), ns));
      }

      if (antennaPropertiesList == null) {
        LOGGER.info("antennaPropertiesList not set");
      } else {
        for (AntennaProperties field : antennaPropertiesList) {
          root.addContent(
              field.encodeXML(
                  field
                      .getClass()
                      .getName()
                      .replaceAll(field.getClass().getPackage().getName() + ".", ""),
                  ns));
        }
      }

      if (antennaConfigurationList == null) {
        LOGGER.info("antennaConfigurationList not set");
      } else {
        for (AntennaConfiguration field : antennaConfigurationList) {
          root.addContent(
              field.encodeXML(
                  field
                      .getClass()
                      .getName()
                      .replaceAll(field.getClass().getPackage().getName() + ".", ""),
                  ns));
        }
      }

      if (rOReportSpec == null) {
        LOGGER.info("rOReportSpec not set");
      } else {
        root.addContent(rOReportSpec.encodeXML(rOReportSpec.getClass().getSimpleName(), ns));
      }

      if (accessReportSpec == null) {
        LOGGER.info("accessReportSpec not set");
      } else {
        root.addContent(
            accessReportSpec.encodeXML(accessReportSpec.getClass().getSimpleName(), ns));
      }

      if (keepaliveSpec == null) {
        LOGGER.info("keepaliveSpec not set");
      } else {
        root.addContent(keepaliveSpec.encodeXML(keepaliveSpec.getClass().getSimpleName(), ns));
      }

      if (gPOWriteDataList == null) {
        LOGGER.info("gPOWriteDataList not set");
      } else {
        for (GPOWriteData field : gPOWriteDataList) {
          root.addContent(
              field.encodeXML(
                  field
                      .getClass()
                      .getName()
                      .replaceAll(field.getClass().getPackage().getName() + ".", ""),
                  ns));
        }
      }

      if (gPIPortCurrentStateList == null) {
        LOGGER.info("gPIPortCurrentStateList not set");
      } else {
        for (GPIPortCurrentState field : gPIPortCurrentStateList) {
          root.addContent(
              field.encodeXML(
                  field
                      .getClass()
                      .getName()
                      .replaceAll(field.getClass().getPackage().getName() + ".", ""),
                  ns));
        }
      }

      if (eventsAndReports == null) {
        LOGGER.info("eventsAndReports not set");
      } else {
        root.addContent(
            eventsAndReports.encodeXML(eventsAndReports.getClass().getSimpleName(), ns));
      }

      if (customList == null) {
        LOGGER.info("customList not set");
      } else {
        for (Custom field : customList) {
          root.addContent(
              field.encodeXML(
                  field
                      .getClass()
                      .getName()
                      .replaceAll(field.getClass().getPackage().getName() + ".", ""),
                  ns));
        }
      }

      Document doc = new Document(root);

      if (isValidXMLMessage(doc, LLRPConstants.LLRPMESSAGESCHEMAPATH)) {
        return doc;
      } else {
        return null;
      }
    } catch (IllegalArgumentException e) {
      throw new InvalidLLRPMessageException(e.getMessage());
    } catch (MissingParameterException e) {
      throw new InvalidLLRPMessageException(e.getMessage());
    }
  }
  /** {@inheritDoc} */
  protected void decodeBinarySpecific(LLRPBitList binary) throws InvalidLLRPMessageException {
    int position = 0;
    int tempByteLength;
    int tempLength = 0;
    int count;
    SignedShort type;
    int fieldCount;
    Custom custom;
    resetToFactoryDefault = new Bit(binary.subList(position, Bit.length()));
    position += Bit.length();
    position += reserved0.length();

    // look ahead to see type
    // may be optional or exactly once
    type = null;
    tempByteLength = 0;
    tempLength = 0;

    try {
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }
    } catch (IllegalArgumentException le) {
      // if an IllegalArgumentException is thrown, list was not long enough so the parameter is
      // missing
      LOGGER.info(
          "SET_READER_CONFIG misses optional parameter of type ReaderEventNotificationSpec");
    }

    if (binary.get(position)) {
      // length can statically be determined for TV Parameters
      tempLength = readerEventNotificationSpec.length();
    }

    if ((type != null) && type.equals(ReaderEventNotificationSpec.TYPENUM)) {
      readerEventNotificationSpec =
          new ReaderEventNotificationSpec(binary.subList(position, tempLength));
      position += tempLength;
      LOGGER.debug(
          " readerEventNotificationSpec is instantiated with ReaderEventNotificationSpec with length"
              + tempLength);
    } else {
      LOGGER.info(
          "SET_READER_CONFIG misses optional parameter of type ReaderEventNotificationSpec");
    }

    // list of parameters
    antennaPropertiesList = new LinkedList<AntennaProperties>();
    LOGGER.debug("decoding parameter antennaPropertiesList ");

    while (position < binary.length()) {
      // store if one parameter matched
      boolean atLeastOnce = false;

      // look ahead to see type
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }

      // add parameter to list if type number matches
      if ((type != null) && type.equals(AntennaProperties.TYPENUM)) {
        // if first bit is 1 it is a TV Parameter
        if (binary.get(position)) {
          // length can statically be determined for TV Parameters
          tempLength = AntennaProperties.length();
        }

        antennaPropertiesList.add(new AntennaProperties(binary.subList(position, tempLength)));
        LOGGER.debug("adding AntennaProperties to antennaPropertiesList ");
        atLeastOnce = true;
        position += tempLength;
      }

      if (!atLeastOnce) {
        // no parameter matched therefore we jump out of the loop
        break;
      }
    }

    // if list is still empty no parameter matched
    if (antennaPropertiesList.isEmpty()) {
      LOGGER.info("encoded message does not contain parameter for optional antennaPropertiesList");
    }

    // list of parameters
    antennaConfigurationList = new LinkedList<AntennaConfiguration>();
    LOGGER.debug("decoding parameter antennaConfigurationList ");

    while (position < binary.length()) {
      // store if one parameter matched
      boolean atLeastOnce = false;

      // look ahead to see type
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }

      // add parameter to list if type number matches
      if ((type != null) && type.equals(AntennaConfiguration.TYPENUM)) {
        // if first bit is 1 it is a TV Parameter
        if (binary.get(position)) {
          // length can statically be determined for TV Parameters
          tempLength = AntennaConfiguration.length();
        }

        antennaConfigurationList.add(
            new AntennaConfiguration(binary.subList(position, tempLength)));
        LOGGER.debug("adding AntennaConfiguration to antennaConfigurationList ");
        atLeastOnce = true;
        position += tempLength;
      }

      if (!atLeastOnce) {
        // no parameter matched therefore we jump out of the loop
        break;
      }
    }

    // if list is still empty no parameter matched
    if (antennaConfigurationList.isEmpty()) {
      LOGGER.info(
          "encoded message does not contain parameter for optional antennaConfigurationList");
    }

    // look ahead to see type
    // may be optional or exactly once
    type = null;
    tempByteLength = 0;
    tempLength = 0;

    try {
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }
    } catch (IllegalArgumentException le) {
      // if an IllegalArgumentException is thrown, list was not long enough so the parameter is
      // missing
      LOGGER.info("SET_READER_CONFIG misses optional parameter of type ROReportSpec");
    }

    if (binary.get(position)) {
      // length can statically be determined for TV Parameters
      tempLength = rOReportSpec.length();
    }

    if ((type != null) && type.equals(ROReportSpec.TYPENUM)) {
      rOReportSpec = new ROReportSpec(binary.subList(position, tempLength));
      position += tempLength;
      LOGGER.debug(" rOReportSpec is instantiated with ROReportSpec with length" + tempLength);
    } else {
      LOGGER.info("SET_READER_CONFIG misses optional parameter of type ROReportSpec");
    }

    // look ahead to see type
    // may be optional or exactly once
    type = null;
    tempByteLength = 0;
    tempLength = 0;

    try {
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }
    } catch (IllegalArgumentException le) {
      // if an IllegalArgumentException is thrown, list was not long enough so the parameter is
      // missing
      LOGGER.info("SET_READER_CONFIG misses optional parameter of type AccessReportSpec");
    }

    if (binary.get(position)) {
      // length can statically be determined for TV Parameters
      tempLength = accessReportSpec.length();
    }

    if ((type != null) && type.equals(AccessReportSpec.TYPENUM)) {
      accessReportSpec = new AccessReportSpec(binary.subList(position, tempLength));
      position += tempLength;
      LOGGER.debug(
          " accessReportSpec is instantiated with AccessReportSpec with length" + tempLength);
    } else {
      LOGGER.info("SET_READER_CONFIG misses optional parameter of type AccessReportSpec");
    }

    // look ahead to see type
    // may be optional or exactly once
    type = null;
    tempByteLength = 0;
    tempLength = 0;

    try {
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }
    } catch (IllegalArgumentException le) {
      // if an IllegalArgumentException is thrown, list was not long enough so the parameter is
      // missing
      LOGGER.info("SET_READER_CONFIG misses optional parameter of type KeepaliveSpec");
    }

    if (binary.get(position)) {
      // length can statically be determined for TV Parameters
      tempLength = keepaliveSpec.length();
    }

    if ((type != null) && type.equals(KeepaliveSpec.TYPENUM)) {
      keepaliveSpec = new KeepaliveSpec(binary.subList(position, tempLength));
      position += tempLength;
      LOGGER.debug(" keepaliveSpec is instantiated with KeepaliveSpec with length" + tempLength);
    } else {
      LOGGER.info("SET_READER_CONFIG misses optional parameter of type KeepaliveSpec");
    }

    // list of parameters
    gPOWriteDataList = new LinkedList<GPOWriteData>();
    LOGGER.debug("decoding parameter gPOWriteDataList ");

    while (position < binary.length()) {
      // store if one parameter matched
      boolean atLeastOnce = false;

      // look ahead to see type
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }

      // add parameter to list if type number matches
      if ((type != null) && type.equals(GPOWriteData.TYPENUM)) {
        // if first bit is 1 it is a TV Parameter
        if (binary.get(position)) {
          // length can statically be determined for TV Parameters
          tempLength = GPOWriteData.length();
        }

        gPOWriteDataList.add(new GPOWriteData(binary.subList(position, tempLength)));
        LOGGER.debug("adding GPOWriteData to gPOWriteDataList ");
        atLeastOnce = true;
        position += tempLength;
      }

      if (!atLeastOnce) {
        // no parameter matched therefore we jump out of the loop
        break;
      }
    }

    // if list is still empty no parameter matched
    if (gPOWriteDataList.isEmpty()) {
      LOGGER.info("encoded message does not contain parameter for optional gPOWriteDataList");
    }

    // list of parameters
    gPIPortCurrentStateList = new LinkedList<GPIPortCurrentState>();
    LOGGER.debug("decoding parameter gPIPortCurrentStateList ");

    while (position < binary.length()) {
      // store if one parameter matched
      boolean atLeastOnce = false;

      // look ahead to see type
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }

      // add parameter to list if type number matches
      if ((type != null) && type.equals(GPIPortCurrentState.TYPENUM)) {
        // if first bit is 1 it is a TV Parameter
        if (binary.get(position)) {
          // length can statically be determined for TV Parameters
          tempLength = GPIPortCurrentState.length();
        }

        gPIPortCurrentStateList.add(new GPIPortCurrentState(binary.subList(position, tempLength)));
        LOGGER.debug("adding GPIPortCurrentState to gPIPortCurrentStateList ");
        atLeastOnce = true;
        position += tempLength;
      }

      if (!atLeastOnce) {
        // no parameter matched therefore we jump out of the loop
        break;
      }
    }

    // if list is still empty no parameter matched
    if (gPIPortCurrentStateList.isEmpty()) {
      LOGGER.info(
          "encoded message does not contain parameter for optional gPIPortCurrentStateList");
    }

    // look ahead to see type
    // may be optional or exactly once
    type = null;
    tempByteLength = 0;
    tempLength = 0;

    try {
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }
    } catch (IllegalArgumentException le) {
      // if an IllegalArgumentException is thrown, list was not long enough so the parameter is
      // missing
      LOGGER.info("SET_READER_CONFIG misses optional parameter of type EventsAndReports");
    }

    if (binary.get(position)) {
      // length can statically be determined for TV Parameters
      tempLength = eventsAndReports.length();
    }

    if ((type != null) && type.equals(EventsAndReports.TYPENUM)) {
      eventsAndReports = new EventsAndReports(binary.subList(position, tempLength));
      position += tempLength;
      LOGGER.debug(
          " eventsAndReports is instantiated with EventsAndReports with length" + tempLength);
    } else {
      LOGGER.info("SET_READER_CONFIG misses optional parameter of type EventsAndReports");
    }

    // list of parameters
    customList = new LinkedList<Custom>();
    LOGGER.debug("decoding parameter customList ");

    while (position < binary.length()) {
      // store if one parameter matched
      boolean atLeastOnce = false;

      // look ahead to see type
      // if first bit is one it is a TV Parameter
      if (binary.get(position)) {
        // do not take the first bit as it is always 1
        type = new SignedShort(binary.subList(position + 1, 7));
      } else {
        type = new SignedShort(binary.subList(position + RESERVEDLENGTH, TYPENUMBERLENGTH));
        tempByteLength =
            new UnsignedShort(
                    binary.subList(
                        position + RESERVEDLENGTH + TYPENUMBERLENGTH, UnsignedShort.length()))
                .toShort();
        tempLength = 8 * tempByteLength;
      }

      // custom
      if ((type != null) && type.equals(Custom.TYPENUM)) {
        Custom cus = new Custom(binary.subList(position, tempLength));
        // allowed custom parameters for this parameter
        // end allowed parameters
        // if none matched continue wasn't called and we add just cus as we found no specific vendor
        // implementation
        customList.add(cus);
        position += tempLength;
        atLeastOnce = true;
      }

      if (!atLeastOnce) {
        // no parameter matched therefore we jump out of the loop
        break;
      }
    }

    // if list is still empty no parameter matched
    if (customList.isEmpty()) {
      LOGGER.info("encoded message does not contain parameter for optional customList");
    }
  }
  /** {@inheritDoc} */
  protected LLRPBitList encodeBinarySpecific() throws InvalidLLRPMessageException {
    LLRPBitList resultBits = new LLRPBitList();

    if (resetToFactoryDefault == null) {
      LOGGER.warn(" resetToFactoryDefault not set");
      throw new InvalidLLRPMessageException(
          " resetToFactoryDefault not set  for Parameter of Type SET_READER_CONFIG");
    }

    resultBits.append(resetToFactoryDefault.encodeBinary());
    resultBits.append(reserved0.encodeBinary());

    if (readerEventNotificationSpec == null) {
      // optional parameter, may be null
      LOGGER.info(" readerEventNotificationSpec not set");
    } else {
      resultBits.append(readerEventNotificationSpec.encodeBinary());
    }

    if (antennaPropertiesList == null) {
      // just warn - it is optional
      LOGGER.info(" antennaPropertiesList not set");
    } else {
      for (AntennaProperties field : antennaPropertiesList) {
        resultBits.append(field.encodeBinary());
      }
    }

    if (antennaConfigurationList == null) {
      // just warn - it is optional
      LOGGER.info(" antennaConfigurationList not set");
    } else {
      for (AntennaConfiguration field : antennaConfigurationList) {
        resultBits.append(field.encodeBinary());
      }
    }

    if (rOReportSpec == null) {
      // optional parameter, may be null
      LOGGER.info(" rOReportSpec not set");
    } else {
      resultBits.append(rOReportSpec.encodeBinary());
    }

    if (accessReportSpec == null) {
      // optional parameter, may be null
      LOGGER.info(" accessReportSpec not set");
    } else {
      resultBits.append(accessReportSpec.encodeBinary());
    }

    if (keepaliveSpec == null) {
      // optional parameter, may be null
      LOGGER.info(" keepaliveSpec not set");
    } else {
      resultBits.append(keepaliveSpec.encodeBinary());
    }

    if (gPOWriteDataList == null) {
      // just warn - it is optional
      LOGGER.info(" gPOWriteDataList not set");
    } else {
      for (GPOWriteData field : gPOWriteDataList) {
        resultBits.append(field.encodeBinary());
      }
    }

    if (gPIPortCurrentStateList == null) {
      // just warn - it is optional
      LOGGER.info(" gPIPortCurrentStateList not set");
    } else {
      for (GPIPortCurrentState field : gPIPortCurrentStateList) {
        resultBits.append(field.encodeBinary());
      }
    }

    if (eventsAndReports == null) {
      // optional parameter, may be null
      LOGGER.info(" eventsAndReports not set");
    } else {
      resultBits.append(eventsAndReports.encodeBinary());
    }

    if (customList == null) {
      // just warn - it is optional
      LOGGER.info(" customList not set");
    } else {
      for (Custom field : customList) {
        resultBits.append(field.encodeBinary());
      }
    }

    return resultBits;
  }