Package org.waveprotocol.wave.model.conversation

Source Code of org.waveprotocol.wave.model.conversation.WaveBasedConversationView

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.adt.ObservableSingleton;
import org.waveprotocol.wave.model.adt.docbased.DocumentBasedSingleton;
import org.waveprotocol.wave.model.document.ObservableDocument;
import org.waveprotocol.wave.model.document.util.DefaultDocEventRouter;
import org.waveprotocol.wave.model.document.util.DocEventRouter;
import org.waveprotocol.wave.model.id.IdConstants;
import org.waveprotocol.wave.model.id.IdGenerator;
import org.waveprotocol.wave.model.id.IdUtil;
import org.waveprotocol.wave.model.id.ModernIdSerialiser;
import org.waveprotocol.wave.model.id.WaveId;
import org.waveprotocol.wave.model.id.WaveletId;
import org.waveprotocol.wave.model.util.CollectionUtils;
import org.waveprotocol.wave.model.util.CopyOnWriteSet;
import org.waveprotocol.wave.model.util.IdentityMap;
import org.waveprotocol.wave.model.util.Preconditions;
import org.waveprotocol.wave.model.wave.ObservableWavelet;
import org.waveprotocol.wave.model.wave.WaveViewListener;
import org.waveprotocol.wave.model.wave.Wavelet;
import org.waveprotocol.wave.model.wave.opbased.ObservableWaveView;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;

/**
* A conversation view backed by a wave.
*
* @author anorth@google.com (Alex North)
*/
public class WaveBasedConversationView implements ObservableConversationView, WaveViewListener {
  /**
   * Wraps a wavelet to provide a conversation, or not, depending
   * on whether it has a manifest.
   */
  private final class ConversationContainer implements
      ObservableSingleton.Listener<DocumentBasedManifest> {
    private final ObservableWavelet wavelet;
    private final ObservableSingleton<DocumentBasedManifest, Void> manifestContainer;
    private WaveletBasedConversation conversation;

    ConversationContainer(ObservableWavelet wavelet,
        ObservableSingleton<DocumentBasedManifest, Void> manifestContainer) {
      this.wavelet = wavelet;
      this.manifestContainer = manifestContainer;
    }

    ObservableWavelet getWavelet() {
      return wavelet;
    }

    WaveletBasedConversation getConversation() {
      if (conversation == null) {
        if (manifestContainer.hasValue()) {
          conversation = WaveletBasedConversation.create(
              WaveBasedConversationView.this, wavelet, manifestContainer.get(), idGenerator);
        }
      }
      return conversation;
    }

    @Override
    public void onValueChanged(DocumentBasedManifest oldValue, DocumentBasedManifest newValue) {
      if (oldValue != null) {
        assert conversation != null;
        conversations.remove(wavelet);
        conversation.destroy();
        triggerOnConversationRemoved(conversation);
        conversation = null;
      }
      if (newValue != null) {
        assert conversation == null;
        conversation = WaveletBasedConversation.create(WaveBasedConversationView.this, wavelet,
            newValue, idGenerator);
        conversations.put(wavelet, conversation);
        triggerOnConversationAdded(conversation);
      }
    }
  }

  /** Id of this group of conversations. */
  private final String id;

  /** Backing wave view. */
  private final ObservableWaveView waveView;

  /** Generator for new ids. */
  private final IdGenerator idGenerator;

  /** A container for every conversational wavelet in view. */
  private final IdentityMap<Wavelet, ConversationContainer> containers =
      CollectionUtils.createIdentityMap();

  /** Conversation adapters keyed by their wavelets. */
  private final Map<Wavelet, WaveletBasedConversation> conversations = CollectionUtils.newHashMap();

  private final CopyOnWriteSet<Listener> listeners = CopyOnWriteSet.create();

  /**
   * Creates a conversation view on an wave view.
   */
  public static WaveBasedConversationView create(ObservableWaveView waveView,
      IdGenerator idGenerator) {
    String id = idFor(waveView.getWaveId());
    WaveBasedConversationView convView = new WaveBasedConversationView(id, waveView, idGenerator);
    waveView.addListener(convView);
    return convView;
  }

  /**
   * Computes the conversation view id for a wave.
   */
  public static String idFor(WaveId wave) {
    return ModernIdSerialiser.INSTANCE.serialiseWaveId(wave);
  }

  private WaveBasedConversationView(
      String id, ObservableWaveView waveView, IdGenerator idGenerator) {
    this.id = id;
    this.waveView = waveView;
    this.idGenerator = idGenerator;

    for (ObservableWavelet wavelet : waveView.getWavelets()) {
      if (IdUtil.isConversationalId(wavelet.getId())) {
        createContainer(wavelet);
      }
    }
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public Collection<WaveletBasedConversation> getConversations() {
    return Collections.unmodifiableCollection(conversations.values());
  }

  @Override
  public WaveletBasedConversation getConversation(String conversationId) {
    return getConversation(WaveletBasedConversation.widFor(conversationId));
  }

  @Override
  public WaveletBasedConversation getRoot() {
    ObservableWavelet rootWavelet = waveView.getRoot();
    return (rootWavelet != null) ? conversations.get(rootWavelet) : null;
  }

  @Override
  public WaveletBasedConversation createRoot() {
    ObservableWavelet rootWavelet = waveView.createRoot();
    return createNewConversation(rootWavelet);
  }

  @Override
  public WaveletBasedConversation createConversation() {
    ObservableWavelet wavelet;
    wavelet = waveView.createWavelet();
    return createNewConversation(wavelet);
  }

  // WaveViewListener.

  @Override
  public void onWaveletAdded(ObservableWavelet wavelet) {
    if (IdUtil.isConversationalId(wavelet.getId())) {
      createContainer(wavelet);
    }
  }

  @Override
  public void onWaveletRemoved(ObservableWavelet wavelet) {
    containers.remove(wavelet);
    WaveletBasedConversation conversation = conversations.remove(wavelet);
    if (conversation != null) {
      triggerOnConversationRemoved(conversation);
    }
  }

  @Override
  public void addListener(Listener listener) {
    listeners.add(listener);
  }

  @Override
  public void removeListener(Listener listener) {
    listeners.remove(listener);
  }

  /**
   * Gets the wave view backing this conversation view.
   */
  public ObservableWaveView getWaveView() {
    return waveView;
  }

  /**
   * Gets the conversation backed by a wavelet; null if the wavelet is not in
   * view or does not have conversation structure.
   */
  public WaveletBasedConversation getConversation(WaveletId id) {
    if (!IdUtil.isConversationalId(id)) {
      Preconditions.illegalArgument("Wavelet id " + id + " is not conversational");
    }
    ObservableWavelet wavelet = waveView.getWavelet(id);
    if (wavelet != null) {
      return conversations.get(wavelet);
    }
    return null;
  }

  /**
   * Creates and stores a conversation container. If the container has a
   * conversation then initializes that too.
   */
  private ConversationContainer createContainer(ObservableWavelet wavelet) {
    ObservableDocument manifestDoc = wavelet.getDocument(IdConstants.MANIFEST_DOCUMENT_ID);
    DocEventRouter router = DefaultDocEventRouter.create(manifestDoc);
    ObservableSingleton<DocumentBasedManifest, Void> manifestContainer =
        DocumentBasedSingleton.create(router, manifestDoc.getDocumentElement(),
           DocumentBasedManifest.MANIFEST_TOP_TAG, DocumentBasedManifest.FACTORY);
    ConversationContainer container = new ConversationContainer(wavelet, manifestContainer);
    manifestContainer.addListener(container);
    containers.put(wavelet, container);

    WaveletBasedConversation conversation = container.getConversation();
    if (conversation != null) {
      conversations.put(wavelet, conversation);
      triggerOnConversationAdded(conversation);
    }

    return container;
  }

  /**
   * Creates a new conversation model on a wavelet.
   *
   * @param wavelet wavelet with which to back the conversation
   * @return a conversation
   */
  private WaveletBasedConversation createNewConversation(ObservableWavelet wavelet) {
    assert IdUtil.isConversationalId(wavelet.getId());
    // Add conversation structure to the wavelet, then get the conversation
    // which may be constructed in the waiter.
    WaveletBasedConversation.makeWaveletConversational(wavelet);
    // The container will have created the conversation.
    assert conversations.containsKey(wavelet);
    return conversations.get(wavelet);
  }

  private void triggerOnConversationAdded(WaveletBasedConversation conversation) {
    for (Listener l : listeners) {
      l.onConversationAdded(conversation);
    }
  }

  private void triggerOnConversationRemoved(WaveletBasedConversation conversation) {
    for (Listener l : listeners) {
      l.onConversationRemoved(conversation);
    }
  }
}
TOP

Related Classes of org.waveprotocol.wave.model.conversation.WaveBasedConversationView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.