/** * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * */ package org.waveprotocol.wave.model.conversation; import org.waveprotocol.wave.model.conversation.Conversation.Anchor; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.IdentityMap; import java.util.Collection; import java.util.Collections; /** * Exposes the structure in a conversation view. This structure is not live; it * does not remain synchronized with the conversation structure as it changes. */ public final class ConversationStructure { /** Maps blips to conversations anchored at that blip. Never null. */ private final IdentityMap<ConversationBlip, Collection<Conversation>> anchoring; /** Collection of non-root conversations not attached to any blip. Never null. */ private final Collection<Conversation> unanchored; /** The main conversation, if there is one. */ private final Conversation mainConversation; /** * Creates a conversation structure. */ private ConversationStructure(IdentityMap<ConversationBlip, Collection<Conversation>> anchoring, Collection<Conversation> unanchored, Conversation mainConversation) { this.anchoring = anchoring; this.unanchored = unanchored; this.mainConversation = mainConversation; } /** @return the structure of a conversations in {@code wave}. */ public static ConversationStructure of(ConversationView wave) { IdentityMap<ConversationBlip, Collection<Conversation>> anchoring = null; Collection<Conversation> unanchored = null; Conversation mainConversation = getMainConversation(wave); for (Conversation conversation : wave.getConversations()) { if (conversation == mainConversation) { continue; } Anchor anchor = conversation.getAnchor(); ConversationBlip blip = anchor != null ? anchor.getBlip() : null; if (blip != null) { addLazily((anchoring = createIfNull(anchoring)), blip, conversation); } else { (unanchored = createIfNull(unanchored)).add(conversation); } } if (anchoring == null) { anchoring = CollectionUtils.emptyIdentityMap(); } if (unanchored == null) { unanchored = Collections.emptySet(); } return new ConversationStructure(anchoring, unanchored, mainConversation); } /** * Adds an item to the value collection in a map, creating the collection if * it does not yet exist. */ private static <K, V> void addLazily(IdentityMap<K, Collection<V>> map, K key, V value) { Collection<V> list = map.get(key); if (list == null) { list = CollectionUtils.createQueue(); map.put(key, list); } list.add(value); } /** @return a non-null version of {@code list}. */ private static <T> Collection<T> createIfNull(Collection<T> list) { return list != null ? list : CollectionUtils.<T> createQueue(); } /** @return a non-null version of {@code map}. */ private static <K, V> IdentityMap<K, V> createIfNull(IdentityMap<K, V> map) { return map != null ? map : CollectionUtils.<K, V> createIdentityMap(); } /** @return the conversations anchored at {@code blip}. Never null. */ public Collection<Conversation> getAnchoredConversations(ConversationBlip blip) { Collection<Conversation> anchored = anchoring.get(blip); return anchored != null ? anchored : Collections.<Conversation> emptySet(); } /** @return the non-root conversations not anchored to any blip. */ public Collection<Conversation> getUnanchored() { return unanchored; } /** @return the main conversation in this wave. */ public Conversation getMainConversation() { return mainConversation; } /** * Finds the main conversation in a wave. The main conversation is defined as * the root if there is one, or the first unanchored conversation otherwise. */ public static Conversation getMainConversation(ConversationView view) { Conversation root = view.getRoot(); if (root == null) { for (Conversation curr : view.getConversations()) { if (!curr.hasAnchor()) { root = curr; break; } } } return root; } }