@Override
  protected boolean matchesSafely(final Metadata metadata, final Description description) {
    if (idIsSetAndDoesNotMatchWith(metadata)) {
      description.appendText("Metadata with id = " + metadata.id());
      return false;
    }

    if (nameIsSetAndDoesNotMatchWith(metadata)) {
      description.appendText("Metadata with name = " + metadata.name());
      return false;
    }

    if (causationIdsDoNotMatchWith(metadata)) {
      description.appendText("Metadata with causationIds = " + metadata.causation());
      return false;
    }

    if (userIdIsSetAndDoesNotMatchWith(metadata)) {
      description.appendText("Metadata with userId = " + metadata.userId().orElse(NOT_SET));
      return false;
    }

    if (sessionIdIsSetAndDoesNotMatchWith(metadata)) {
      description.appendText("Metadata with sessionId = " + metadata.sessionId().orElse(NOT_SET));
      return false;
    }

    if (streamIdIsSetAndDoesNotMatchWith(metadata)) {
      description.appendText(
          "Metadata with streamId = " + metadata.streamId().map(UUID::toString).orElse(NOT_SET));
      return false;
    }

    if (versionIsSetAndDoesNotMatchWith(metadata)) {
      description.appendText(
          "Metadata with version = " + metadata.version().map(String::valueOf).orElse(NOT_SET));
      return false;
    }

    if (clientCorrelationIdIsSetAndDoesNotMatchWith(metadata)) {
      description.appendText(
          "Metadata with clientCorrelationId = "
              + metadata.clientCorrelationId().map(String::valueOf).orElse(NOT_SET));
      return false;
    }

    if (jsonMatcherIsSetAndDoesNotMatchWith(metadata)) {
      description.appendText("Metadata ");
      jsonMatcher.ifPresent(
          matcher -> matcher.describeMismatch(metadata.asJsonObject().toString(), description));
      return false;
    }

    return true;
  }
  /**
   * Directly match a given metadata instance
   *
   * @param metadata the metadata to match
   * @return the matcher instance
   */
  public JsonEnvelopeMetadataMatcher of(final Metadata metadata) {
    id = Optional.of(metadata.id());
    name = Optional.of(metadata.name());
    userId = metadata.userId();
    sessionId = metadata.sessionId();
    streamId = metadata.streamId();
    version = metadata.version();

    final List<UUID> causation = metadata.causation();
    causationIds = Optional.of(causation.toArray(new UUID[causation.size()]));

    return this;
  }
  /**
   * Does a match of a given metadata instance as though the JsonEnvelope was enveloped using the
   * given metadata. The id and name are ignored, and the id is added to the causation id list.
   *
   * @param metadata the metadata to match
   * @return the matcher instance
   */
  public JsonEnvelopeMetadataMatcher envelopedWith(final Metadata metadata) {
    id = Optional.empty();
    name = Optional.empty();
    userId = metadata.userId();
    sessionId = metadata.sessionId();
    streamId = metadata.streamId();
    version = metadata.version();

    final List<UUID> causation = metadata.causation();
    final UUID[] uuids = causation.toArray(new UUID[causation.size() + 1]);
    uuids[uuids.length - 1] = metadata.id();
    causationIds = Optional.of(uuids);

    return this;
  }
 private boolean jsonMatcherIsSetAndDoesNotMatchWith(final Metadata metadata) {
   return jsonMatcher.isPresent()
       && !jsonMatcher.get().matches(metadata.asJsonObject().toString());
 }
 private boolean clientCorrelationIdIsSetAndDoesNotMatchWith(final Metadata metadata) {
   return clientCorrelationId.isPresent()
       && !clientCorrelationId.equals(metadata.clientCorrelationId());
 }
 private boolean versionIsSetAndDoesNotMatchWith(final Metadata metadata) {
   return version.isPresent() && !version.equals(metadata.version());
 }
 private boolean streamIdIsSetAndDoesNotMatchWith(final Metadata metadata) {
   return streamId.isPresent() && !streamId.equals(metadata.streamId());
 }
 private boolean userIdIsSetAndDoesNotMatchWith(final Metadata metadata) {
   return userId.isPresent() && !userId.equals(metadata.userId());
 }
 private boolean causationIdsDoNotMatchWith(final Metadata metadata) {
   return causationIds.isPresent()
       && !containsInAnyOrder(causationIds.get()).matches(metadata.causation());
 }
 private boolean nameIsSetAndDoesNotMatchWith(final Metadata metadata) {
   return name.isPresent() && !name.get().equals(metadata.name());
 }
 private boolean idIsSetAndDoesNotMatchWith(final Metadata metadata) {
   return id.isPresent() && !id.get().equals(metadata.id());
 }