private void observe(WaveletBasedConversationBlip blip) { blips.put(blip.getId(), blip); blip.addListener(new BlipListenerAggregator(blip)); for (WaveletBasedConversationThread thread : blip.getReplyThreads()) { observe(thread); } }
/** * Invalidates this blip. It may no longer be accessed. */ void invalidate() { checkIsUsable(); manifestBlip.removeListener(this); isUsable = false; }
@Override public Blip hackGetRaw() { return getBlip(); }
@Override public void onDeleted() { blip.removeListener(this); triggerOnBlipDeleted(blip); blips.remove(blip.getId()); }
/** * Removes a blip from the internal list and triggers its deletion event. */ private void forgetBlip(WaveletBasedConversationBlip blipToRemove) { String idToRemove = blipToRemove.getId(); assert blips.containsKey(idToRemove); blips.remove(idToRemove); blipToRemove.triggerOnDeleted(); }
public void testConcrrentDeletionOfFinalBlipsLeavesEmptyThread() { WaveletBasedConversationBlip first = target.getRootThread().appendBlip(); WaveletBasedConversationThread replyThread = first.addReplyThread(); WaveletBasedConversationBlip b1 = replyThread.appendBlip(); WaveletBasedConversationBlip b2 = replyThread.appendBlip(); // Locally delete b1, remotely delete b2. b1.delete(); replyThread.addListener(threadListener); b2.addListener(blipListener); target.addListener(convListener); remoteRemoveBlip(b2); // Expect blip deletion events and it to be invalid. verify(blipListener).onDeleted(); verify(convListener).onBlipDeleted(b2); assertBlipInvalid(b2); assertThreadValid(replyThread); assertEquals(Arrays.asList(replyThread), CollectionUtils.newArrayList(first.getReplyThreads())); // The manifest now has a thread with no blips. assertEquals(1, first.getManifestBlip().numReplies()); assertEquals(0, first.getManifestBlip().getReply(0).numBlips()); // Still there after the next mutation. ObservableConversationBlip second = target.getRootThread().appendBlip(); assertThreadValid(replyThread); assertEquals(Arrays.asList(replyThread), CollectionUtils.newArrayList(first.getReplyThreads())); verify(convListener).onBlipAdded(second); verifyNoMoreInteractions(blipListener, threadListener, convListener); }
public void testAppendInlineReplyUpdatesManifest() { WaveletBasedConversationBlip blip = target.getRootThread().appendBlip(); WaveletBasedConversationThread reply = blip.addReplyThread(locateAfterLineElement( blip.getContent())); WaveletBasedConversationBlip replyBlip = reply.appendBlip(); assertManifestXml("<blip id=\"" + blip.getId() + "\">" + "<thread id=\"" + reply.getId() + "\" inline=\"true\">" + "<blip id=\"" + replyBlip.getId() + "\"></blip>" + "</thread>" + "</blip>"); assertEquals(Blips.INITIAL_HEAD + "<body><line></line><reply id=\"" + reply.getId() + "\"></reply></body>", XmlStringBuilder.innerXml(blip.getContent()).toString()); assertMirrorConversationEquivalent(); }
public void testAppendBlipsInReplyThreadsUpdatesManifest() { WaveletBasedConversationBlip blip = target.getRootThread().appendBlip(); WaveletBasedConversationThread reply = blip.addReplyThread(); WaveletBasedConversationBlip firstReplyBlip = reply.appendBlip(); WaveletBasedConversationBlip secondReplyBlip = reply.appendBlip(); assertManifestXml("<blip id=\"" + blip.getId() + "\">" + "<thread id=\"" + reply.getId() + "\">" + "<blip id=\"" + firstReplyBlip.getId() + "\"></blip>" + "<blip id=\"" + secondReplyBlip.getId() + "\"></blip>" + "</thread>" + "</blip>"); assertMirrorConversationEquivalent(); }
/** * Deletes a blip from this thread, deleting that blip's replies. */ void deleteBlip(WaveletBasedConversationBlip blipToDelete, boolean shouldDeleteSelfIfEmpty) { Preconditions.checkArgument(blips.containsKey(blipToDelete.getId()), "Can't delete blip not from this thread"); blipToDelete.deleteThreads(); manifestThread.removeBlip(blipToDelete.getManifestBlip()); blipToDelete.clearContent(); if (shouldDeleteSelfIfEmpty) { deleteSelfIfEmpty(); } }
public void testDeleteBlipWithInlineReplyUpdatesManifest() { WaveletBasedConversationBlip blip = target.getRootThread().appendBlip(); WaveletBasedConversationThread reply = blip.addReplyThread( BlipTestUtils.getBodyPosition(blip) + 3); WaveletBasedConversationBlip replyBlip = reply.appendBlip(); blip.delete(); // The first blip is gone, and the inline reply and its blip are gone too. // Both blips' content is gone. assertManifestXml(""); assertStructureEquivalent(XmlStringBuilder.createEmpty(), blip.getContent()); assertStructureEquivalent(XmlStringBuilder.createEmpty(), replyBlip.getContent()); assertMirrorConversationEquivalent(); }
/** * Removes a blip from the manifest, as though a remote client had done so. */ private void remoteRemoveBlip(WaveletBasedConversationBlip blip) { ManifestThread parentThread = blip.getThread().getManifestThread(); ManifestBlip manifestBlip = blip.getManifestBlip(); parentThread.removeBlip(manifestBlip); }
public void testDeleteBlipNoRepliesUpdatesManifest() { WaveletBasedConversationBlip blip = target.getRootThread().appendBlip(); blip.delete(); assertManifestXml(""); assertStructureEquivalent(XmlStringBuilder.createEmpty(), blip.getContent()); assertMirrorConversationEquivalent(); }
/** * Tests copying of a wavelet with a root thread and a reply thread. */ public void testReplyThreadCopy() { WaveletBasedConversationBlip blip = source.getRootThread().appendBlip(); WaveletBasedConversationThread conversationThread = blip.addReplyThread(); Document doc = conversationThread.appendBlip().getContent(); LineContainers.appendToLastLine(doc, XmlStringBuilder.createText(SAMPLE_TEXT)); ConversationCopier.copyWaveletContents(sourceWavelet, destWavelet); compareWavelets(sourceWavelet, destWavelet); }
/** * Removes a thread from the manifest, as though a remote client had done so. */ private void remoteRemoveThread(WaveletBasedConversationThread thread) { ManifestBlip parentBlip = thread.getParentBlip().getManifestBlip(); ManifestThread manifestThread = thread.getManifestThread(); parentBlip.removeReply(manifestThread); }
@Override public WaveletBasedConversationThread addReplyThread(final int location) { checkIsUsable(); final String threadId = helper.createThreadId(); createInlineReplyAnchor(threadId, location); manifestBlip.appendReply(threadId, true); return replies.get(threadId); }
@Override public Iterable<LocatedReplyThread> locateReplyThreads() { // NOTE(anorth): We must recalculate the anchor locations on each // call as the document does not provide stable elements. However, we // calculate the list of anchor locations on demand. Map<String, Integer> replyLocations = null; List<LocatedReplyThread> inlineReplyThreads = CollectionUtils.newArrayList(); for (WaveletBasedConversationThread reply : getReplyThreads()) { if (replyLocations == null) { replyLocations = findAnchors(); } Integer location = replyLocations.get(reply.getId()); inlineReplyThreads.add(new LocatedReplyThread(reply, (location != null) ? location : Blips.INVALID_INLINE_LOCATION)); } Collections.sort(inlineReplyThreads); return Collections.unmodifiableList(inlineReplyThreads); }
@Override public void delete() { checkIsUsable(); Collection<WaveletBasedConversationThread> allReplies = CollectionUtils.createQueue(); CollectionUtils.copyValuesToJavaCollection(replies, allReplies); // Delete reply threads. // TODO(anorth): Move this loop to WBCT, where it can delete all the // inline reply anchors in one pass. for (WaveletBasedConversationThread replyThread : allReplies) { deleteThread(replyThread); } // All replies have been deleted, so remove this empty blip. parentThread.deleteBlip(this, true); }
/** * Deletes all threads from this blip. * * @see WaveletBasedConversationBlip#deleteThread(WaveletBasedConversationThread) */ void deleteThreads() { // deleteThread() equivalent is inline here so we can do only one // document traversal to remove inline reply anchors. List<WaveletBasedConversationThread> threads = CollectionUtils.newArrayList(getReplyThreads()); for (WaveletBasedConversationThread threadToDelete : threads) { threadToDelete.deleteBlips(); manifestBlip.removeReply(threadToDelete.getManifestThread()); } clearAllInlineReplyAnchors(); }
/** * Tests that empty threads are not ignored. */ public void testCreateWithEmptyManifestThreadNotIgnored() { ConversationBlip blip = target.getRootThread().appendBlip(); ConversationThread thread = blip.addReplyThread(); WaveletBasedConversation another = mirrorConversation(target); assertNotNull(another.getRootThread().getFirstBlip()); assertTrue(another.getRootThread().getFirstBlip().getReplyThreads().iterator().hasNext()); }