/**
   * Encodes this entry using the V3 encoding.
   *
   * @param buffer The buffer to encode into.
   * @throws DirectoryException If a problem occurs while attempting to encode the entry.
   */
  private void encodeV1(Entry entry, ByteStringBuilder buffer) throws DirectoryException {
    // The version number will be one byte.
    buffer.appendByte(0x01);

    // TODO: Can we encode the DN directly into buffer?
    byte[] dnBytes = getBytes(entry.getName().toString());
    buffer.appendBERLength(dnBytes.length);
    buffer.appendBytes(dnBytes);

    // Encode number of OCs and 0 terminated names.
    int i = 1;
    ByteStringBuilder bsb = new ByteStringBuilder();
    for (String ocName : entry.getObjectClasses().values()) {
      bsb.appendUtf8(ocName);
      if (i < entry.getObjectClasses().values().size()) {
        bsb.appendByte(0x00);
      }
      i++;
    }
    buffer.appendBERLength(bsb.length());
    buffer.appendBytes(bsb);

    // Encode the user attributes in the appropriate manner.
    encodeV1Attributes(buffer, entry.getUserAttributes());

    // The operational attributes will be encoded in the same way as
    // the user attributes.
    encodeV1Attributes(buffer, entry.getOperationalAttributes());
  }
  /**
   * Test entry.
   *
   * @throws Exception If the test failed unexpectedly.
   */
  @Test
  public void testEntryToAndFromDatabase() throws Exception {
    ensureServerIsUpAndRunning();

    // Convert the test LDIF string to a byte array
    byte[] originalLDIFBytes = StaticUtils.getBytes(ldifString);

    try (final LDIFReader reader =
        new LDIFReader(new LDIFImportConfig(new ByteArrayInputStream(originalLDIFBytes)))) {
      Entry entryBefore, entryAfter;
      while ((entryBefore = reader.readEntry(false)) != null) {
        ByteString bytes =
            ID2Entry.entryToDatabase(entryBefore, new DataConfig(false, false, null));

        entryAfter =
            ID2Entry.entryFromDatabase(bytes, DirectoryServer.getDefaultCompressedSchema());

        // check DN and number of attributes
        assertEquals(entryBefore.getAttributes().size(), entryAfter.getAttributes().size());

        assertEquals(entryBefore.getName(), entryAfter.getName());

        // check the object classes were not changed
        for (String ocBefore : entryBefore.getObjectClasses().values()) {
          ObjectClass objectClass = DirectoryServer.getObjectClass(ocBefore.toLowerCase());
          if (objectClass == null) {
            objectClass = DirectoryServer.getDefaultObjectClass(ocBefore);
          }
          String ocAfter = entryAfter.getObjectClasses().get(objectClass);

          assertEquals(ocBefore, ocAfter);
        }

        // check the user attributes were not changed
        for (AttributeType attrType : entryBefore.getUserAttributes().keySet()) {
          List<Attribute> listBefore = entryBefore.getAttribute(attrType);
          List<Attribute> listAfter = entryAfter.getAttribute(attrType);
          assertThat(listBefore).hasSameSizeAs(listAfter);

          for (Attribute attrBefore : listBefore) {
            boolean found = false;

            for (Attribute attrAfter : listAfter) {
              if (attrAfter.optionsEqual(attrBefore.getOptions())) {
                // Found the corresponding attribute
                assertEquals(attrBefore, attrAfter);
                found = true;
              }
            }

            assertTrue(found);
          }
        }
      }
    }
  }
  /**
   * Encodes this entry using the V3 encoding.
   *
   * @param buffer The buffer to encode into.
   * @throws DirectoryException If a problem occurs while attempting to encode the entry.
   */
  private void encodeV2(Entry entry, ByteStringBuilder buffer, EntryEncodeConfig config)
      throws DirectoryException {
    // The version number will be one byte.
    buffer.appendByte(0x02);

    // Get the encoded respresentation of the config.
    config.encode(buffer);

    // If we should include the DN, then it will be encoded as a
    // one-to-five byte length followed by the UTF-8 byte
    // representation.
    if (!config.excludeDN()) {
      // TODO: Can we encode the DN directly into buffer?
      byte[] dnBytes = getBytes(entry.getName().toString());
      buffer.appendBERLength(dnBytes.length);
      buffer.appendBytes(dnBytes);
    }

    // Encode the object classes in the appropriate manner.
    if (config.compressObjectClassSets()) {
      config.getCompressedSchema().encodeObjectClasses(buffer, entry.getObjectClasses());
    } else {
      // Encode number of OCs and 0 terminated names.
      int i = 1;
      ByteStringBuilder bsb = new ByteStringBuilder();
      for (String ocName : entry.getObjectClasses().values()) {
        bsb.appendUtf8(ocName);
        if (i < entry.getObjectClasses().values().size()) {
          bsb.appendByte(0x00);
        }
        i++;
      }
      buffer.appendBERLength(bsb.length());
      buffer.appendBytes(bsb);
    }

    // Encode the user attributes in the appropriate manner.
    encodeV2Attributes(buffer, entry.getUserAttributes(), config);

    // The operational attributes will be encoded in the same way as
    // the user attributes.
    encodeV2Attributes(buffer, entry.getOperationalAttributes(), config);
  }