@Override
  public WaveletData toWaveletData(
      Wavelet wavelet, Conversation conversation, EventMessageBundle eventMessageBundle) {
    final WaveletData waveletData = new WaveletData();
    waveletData.setCreationTime(wavelet.getCreationTime());
    waveletData.setCreator(wavelet.getCreatorId().getAddress());
    waveletData.setWaveId(ApiIdSerializer.instance().serialiseWaveId(wavelet.getWaveId()));
    waveletData.setWaveletId(ApiIdSerializer.instance().serialiseWaveletId(wavelet.getId()));
    waveletData.setLastModifiedTime(wavelet.getLastModifiedTime());
    waveletData.setParticipants(idsToParticipantIdList(wavelet.getParticipantIds()));
    waveletData.setRootBlipId(conversation.getRootThread().getFirstBlip().getId());
    waveletData.setTitle(getTitle(wavelet, conversation));
    waveletData.setVersion(wavelet.getVersion());

    // Add Data Docs. All data documents are silently name spaced under the
    // robot prefix to avoid conflicts. Any docId containing a '+' will be
    // ignored for now.
    for (String documentId : wavelet.getDocumentIds()) {
      if (IdUtil.isRobotDocId(documentId)) {
        String[] parts = IdUtil.split(documentId);
        if (parts.length == 2) {
          Document document = wavelet.getDocument(documentId);
          String val = XmlStringBuilder.innerXml(document).getXmlString();
          waveletData.setDataDocument(parts[1], val);
        }
      }
    }

    // Add the tags.
    if (wavelet.getDocument(IdConstants.TAGS_DOC_ID) != null) {
      @SuppressWarnings("unchecked")
      TagsDocument tags = new TagsDocument(wavelet.getDocument(IdConstants.TAGS_DOC_ID));
      tags.addListener(
          new TagsDocument.Listener() {
            @Override
            public void onAdd(String tagName) {
              waveletData.addTag(tagName);
            }

            @Override
            public void onRemove(int tagPosition) {
              // Not called.
            }
          });
      tags.processInitialState();
    }

    // Add the participant roles.
    ObservableDocument rolesDocument = wavelet.getDocument(IdConstants.ROLES_DATA_DOC_ID);
    if (rolesDocument != null) {
      DocumentBasedRoles roles = DocumentBasedRoles.create(rolesDocument);
      for (ParticipantId participantId : wavelet.getParticipantIds()) {
        waveletData.setParticipantRole(
            participantId.getAddress(), roles.getRole(participantId).name());
      }
    }
    return waveletData;
  }
 private ParticipantId computeCreator(WaveViewData wave) {
   for (ObservableWaveletData wavelet : wave.getWavelets()) {
     if (IdUtil.isConversationRootWaveletId(wavelet.getWaveletId())) {
       return wavelet.getCreator();
     }
   }
   // If not found creator - compare with UNKNOWN_CREATOR;
   return UNKNOWN_CREATOR;
 }
 private long computeLmt(WaveViewData wave) {
   long lmt = -1;
   for (ObservableWaveletData wavelet : wave.getWavelets()) {
     // Skip non conversational wavelets.
     if (!IdUtil.isConversationalId(wavelet.getWaveletId())) {
       continue;
     }
     lmt = lmt < wavelet.getLastModifiedTime() ? wavelet.getLastModifiedTime() : lmt;
   }
   return lmt;
 }
  /**
   * Verifies whether the wavelet matches the filter criteria.
   *
   * @param wavelet the wavelet.
   * @param user the logged in user.
   * @param withList the list of participants to be used in 'with' filter.
   * @param creatorList the list of participants to be used in 'creator' filter.
   * @param isAllQuery true if the search results should include shared for this domain waves.
   */
  private boolean matches(
      ObservableWaveletData wavelet,
      ParticipantId user,
      ParticipantId sharedDomainParticipantId,
      List<ParticipantId> withList,
      List<ParticipantId> creatorList,
      boolean isAllQuery)
      throws WaveletStateException {
    // If it is user data wavelet for the user - return true.
    if (IdUtil.isUserDataWavelet(wavelet.getWaveletId()) && wavelet.getCreator().equals(user)) {
      return true;
    }
    // Filter by creator. This is the fastest check so we perform it first.
    for (ParticipantId creator : creatorList) {
      if (!creator.equals(wavelet.getCreator())) {
        // Skip.
        return false;
      }
    }
    // The wavelet should have logged in user as participant for 'in:inbox'
    // query.
    if (!isAllQuery && !wavelet.getParticipants().contains(user)) {
      return false;
    }
    // Or if it is an 'all' query - then either logged in user or shared domain
    // participant should be present in the wave.
    if (isAllQuery
        && !WaveletDataUtil.checkAccessPermission(wavelet, user, sharedDomainParticipantId)) {
      return false;
    }
    // If not returned 'false' above - then logged in user is either
    // explicit or implicit participant and therefore has access permission.

    // Now filter by 'with'.
    for (ParticipantId otherUser : withList) {
      if (!wavelet.getParticipants().contains(otherUser)) {
        // Skip.
        return false;
      }
    }
    return true;
  }