Package org.waveprotocol.wave.model.supplement

Source Code of org.waveprotocol.wave.model.supplement.WaveletThreadStateCollection

/**
* 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.supplement;

import org.waveprotocol.wave.model.conversation.WaveletBasedConversation;
import org.waveprotocol.wave.model.document.ObservableMutableDocument;
import org.waveprotocol.wave.model.document.operation.impl.AttributesImpl;
import org.waveprotocol.wave.model.document.util.DocHelper;
import org.waveprotocol.wave.model.document.util.DocumentEventRouter;
import org.waveprotocol.wave.model.id.WaveletId;
import org.waveprotocol.wave.model.supplement.ObservablePrimitiveSupplement.Listener;
import org.waveprotocol.wave.model.util.ElementListener;
import org.waveprotocol.wave.model.util.Preconditions;

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

/**
* Represents a collection of per-wavelet thread-states, implemented by
* embedding them in elements of a document.
*
* @see WaveletReadStateCollection
*
*/
final class WaveletThreadStateCollection<E> implements ElementListener<E> {
  private final DocumentEventRouter<? super E, E, ?> router;
  private final E container;

  /** Read state, expressed as a per-wavelet structure. */
  private final Map<WaveletId, WaveletThreadState> waveletSupplements =
      new HashMap<WaveletId, WaveletThreadState>();

  /** Listener to inject into each read-state. */
  private final Listener listener;

  private WaveletThreadStateCollection(DocumentEventRouter<? super E, E, ?> router,
      E container, Listener listener) {
    this.router = router;
    this.container = container;
    this.listener = listener;
  }

  public static <E> WaveletThreadStateCollection<E> create(
      DocumentEventRouter<? super E, E, ?> router, E e, Listener listener) {
    WaveletThreadStateCollection<E> col = new WaveletThreadStateCollection<E>(router,
        e, listener);
    router.addChildListener(e, col);
    col.load();
    return col;
  }

  private ObservableMutableDocument<? super E, E, ?> getDocument() {
    return router.getDocument();
  }

  private void load() {
    ObservableMutableDocument<? super E, E, ?> doc = getDocument();
    E child = DocHelper.getFirstChildElement(doc, doc.getDocumentElement());
    while (child != null) {
      onElementAdded(child);
      child = DocHelper.getNextSiblingElement(doc, child);
    }
  }

  private WaveletId valueOf(E element) {
    String waveletIdStr = getDocument().getAttribute(element, WaveletBasedSupplement.ID_ATTR);
    return WaveletBasedConversation.widFor(waveletIdStr);
  }

  @Override
  public void onElementAdded(E element) {
    ObservableMutableDocument<? super E, E, ?> doc = getDocument();
    assert container.equals(doc.getParentElement(element));
    if (!WaveletBasedSupplement.CONVERSATION_TAG.equals(doc.getTagName(element))) {
      return;
    }

    WaveletId waveletId = valueOf(element);
    if (waveletId != null) {
      WaveletThreadState existing = waveletSupplements.get(waveletId);
      if (existing == null) {
        WaveletThreadState read =
            DocumentBasedWaveletThreadState.create(router, element, waveletId, listener);
        waveletSupplements.put(waveletId, read);
      } else {
        //
        // We can't mutate during callbacks yet.
        // Let's just ignore the latter :(. Clean up on timer?
        //
      }
    } else {
      // XML error: someone added a WAVELET element without an id. Ignore.
      // TODO(user): we should log this
    }
  }

  public void onElementRemoved(E element) {
    if (WaveletBasedSupplement.CONVERSATION_TAG.equals(getDocument().getTagName(element))) {
      WaveletId waveletId = valueOf(element);
      if (waveletId != null) {
        WaveletThreadState state = waveletSupplements.remove(waveletId);
        if (state == null) {
          // Not good - there was a collapsed-state element and we weren't
          // tracking it...
          // TODO(user): this is the same problem as the read state
          // tracker
        }
      }
    }
  }

  private void createEntry(WaveletId waveletId) {
    String waveletIdStr = WaveletBasedConversation.idFor(waveletId);
    ObservableMutableDocument<? super E, E, ?> doc = getDocument();
    E container =
        doc.createChildElement(doc.getDocumentElement(), WaveletBasedSupplement.CONVERSATION_TAG,
            new AttributesImpl(WaveletBasedSupplement.ID_ATTR, waveletIdStr));
  }

  WaveletThreadState getThreadStateSupplement(WaveletId waveletId) {
    Preconditions.checkNotNull(waveletId, "wavelet id must not be null");
    WaveletThreadState wavelet = waveletSupplements.get(waveletId);
    if (wavelet == null) {
      // Create a new container element for tracking state for the wavelet.
      // Callbacks should build it.
      createEntry(waveletId);
      wavelet = waveletSupplements.get(waveletId);
      assert wavelet != null;
    }
    return wavelet;
  }

  void clear() {
    Collection<WaveletId> toRemove = new ArrayList<WaveletId>(waveletSupplements.keySet());
    for (WaveletId waveletId : toRemove) {
      waveletSupplements.get(waveletId).remove();
    }
  }

  ThreadState getThreadState(WaveletId waveletId, String threadId) {
    WaveletThreadState wavelet = waveletSupplements.get(waveletId);
    return wavelet == null ? null : wavelet.getThreadState(threadId);
  }

  public Iterable<String> getStatefulThreads(WaveletId waveletId) {
    return waveletSupplements.containsKey(waveletId) ? getThreadStateSupplement(waveletId)
        .getThreads() : Collections.<String> emptySet();
  }

  public Iterable<WaveletId> getStatefulWavelets() {
    return waveletSupplements.keySet();
  }

  void setThreadState(WaveletId waveletId, String threadId, ThreadState value) {
    if (value == null) {
      WaveletThreadState wavelet = waveletSupplements.get(waveletId);
      if (wavelet != null) {
         wavelet.setThreadState(threadId, value);
      }
    } else {
       getThreadStateSupplement(waveletId).setThreadState(threadId, value);
    }
  }
}
TOP

Related Classes of org.waveprotocol.wave.model.supplement.WaveletThreadStateCollection

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.