@Override
  public BlipData toBlipData(
      ConversationBlip blip, Wavelet wavelet, EventMessageBundle eventMessageBundle) {
    ConversationBlip parentBlip = findBlipParent(blip);
    BlipData blipData = new BlipData();
    blipData.setCreator(blip.getAuthorId().getAddress());
    blipData.setContributors(idsToParticipantIdList(blip.getContributorIds()));
    blipData.setBlipId(blip.getId());
    blipData.setLastModifiedTime(blip.getLastModifiedTime());
    blipData.setVersion(blip.getLastModifiedVersion());
    blipData.setParentBlipId(parentBlip == null ? null : parentBlip.getId());
    blipData.setWaveId(ApiIdSerializer.instance().serialiseWaveId(wavelet.getWaveId()));
    blipData.setWaveletId(ApiIdSerializer.instance().serialiseWaveletId(wavelet.getId()));
    blipData.setChildBlipIds(toBlipIdList(findBlipChildren(blip)));

    ApiView apiView = new ApiView(blip.getContent(), wavelet);
    // Set content.
    blipData.setContent(apiView.apiContents());
    // Set Annotations.
    blipData.setAnnotations(extractAnnotations(blip.getContent(), apiView));
    // blip.getContent().rangedAnnotations(0, blip.getContent().size(), null),
    // Set Form Elements.
    blipData.setElements(ElementSerializer.serialize(blip.getContent(), wavelet));
    return blipData;
  }
  @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;
  }