Package org.waveprotocol.wave.client.wavepanel.view

Source Code of org.waveprotocol.wave.client.wavepanel.view.ModelIdMapperImpl

/**
* 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.client.wavepanel.view;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;

import org.waveprotocol.wave.client.common.util.StringCodec;
import org.waveprotocol.wave.model.conversation.Conversation;
import org.waveprotocol.wave.model.conversation.ConversationBlip;
import org.waveprotocol.wave.model.conversation.ConversationThread;
import org.waveprotocol.wave.model.conversation.ConversationView;
import org.waveprotocol.wave.model.util.CollectionUtils;
import org.waveprotocol.wave.model.util.Pair;
import org.waveprotocol.wave.model.util.ReadableStringMap.ProcV;
import org.waveprotocol.wave.model.util.StringMap;
import org.waveprotocol.wave.model.wave.ParticipantId;

/**
* Provides an isomorphic mapping between model objects and strings.
*
*/
public final class ModelIdMapperImpl implements ModelIdMapper {

  // Because of the loosely defined alphabets of input strings (conversation
  // ids, blip ids, participant ids), this code assumes that all characters are
  // permitted in those strings. This means that constructing product values
  // (pairs, triples etc) must be done with linear-cost quoting.

  private final static StringCodec CODEC = StringCodec.INSTANCE;

  /** Component separator, as provided by StringCodec. */
  private final static String SEP = CODEC.free().substring(0, 1);

  /** Model context in which this mapper is mapping. */
  private final ConversationView model;

  /** Prefix for short ids, used for namespacing. */
  private final String prefix;

  /** Counter for the next short id. */
  private int nextId;

  /**
   * 2 String map that maps between longId and shortId.
   * There are 2 invariants that hold
   * longIdToShortIdMap.containKey(longId) ->
   *    shortIdToLongIdMap.get(longIdToShortIdMap.get(longId)).equals(longId)
   *
   * shortIdToLongIdMap.containKey(shortId) ->
   *    longIdToShortIdMap.get(shortIdToLongIdMap.get(shortId)).equals(shortId)
   */
  private final StringMap<String> shortIdToLongIdMap = CollectionUtils.createStringMap();
  private final StringMap<String> longIdToShortIdMap = CollectionUtils.createStringMap();

  @VisibleForTesting
  ModelIdMapperImpl(ConversationView model, String prefix, int nextId) {
    this.model = model;
    this.prefix = prefix;
    this.nextId = nextId;
  }

  /**
   * Creates a new mapper.
   *
   * @param model model universe
   * @param prefix prefix on all short ids, for namespacing
   */
  public static ModelIdMapperImpl create(ConversationView model, String prefix) {
    return new ModelIdMapperImpl(model, prefix, 0);
  }

  /**
   * Creates a new mapper.
   *
   * @param model model universe
   * @param prefix prefix on all short ids, for namespacing
   * @param nextId initial counter for id generation
   */
  public static ModelIdMapperImpl create(ConversationView model, String prefix, int nextId) {
    return new ModelIdMapperImpl(model, prefix, nextId);
  }

  @Override
  public String conversationsId(ConversationView cs) {
    Preconditions.checkArgument(model.equals(cs));
    return prefix;
  }

  @Override
  public String convId(Conversation c) {
    return shorten(c.getId());
  }

  @Override
  public String participantId(Conversation c, ParticipantId p) {
    return shorten(longConvId(c) + SEP + CODEC.encode(p.getAddress()));
  }

  @Override
  public String threadId(ConversationThread t) {
    return shorten(longConvId(t.getConversation()) + SEP + CODEC.encode(t.getId()));
  }

  @Override
  public String blipId(ConversationBlip b) {
    return shorten(longConvId(b.getConversation()) + SEP + CODEC.encode(b.getId()));
  }

  private String longConvId(Conversation c) {
    return CODEC.encode(c.getId());
  }

  @Override
  public ConversationBlip locateBlip(String modelId) {
    String longId = restoreId(modelId);
    String[] parts = split(longId);

    if (parts.length != 2) {
      throw new IllegalArgumentException("Not a blip model id: " + modelId);
    } else {
      Conversation c = model.getConversation(parts[0]);
      return c != null ? c.getBlip(parts[1]) : null;
    }
  }

  @Override
  public ConversationThread locateThread(String modelId) {
    String longId = restoreId(modelId);
    String[] parts = split(longId);

    if (parts.length != 2) {
      throw new IllegalArgumentException("Not a thread model id: " + modelId);
    } else {
      Conversation c = model.getConversation(parts[0]);
      return c != null ? c.getThread(parts[1]) : null;
    }
  }

  @Override
  public Conversation locateConversation(String modelId) {
    return model.getConversation(restoreId(modelId));
  }

  @Override
  public Pair<Conversation, ParticipantId> locateParticipant(String modelId) {
    String longId = restoreId(modelId);
    String[] parts = split(longId);

    if (parts.length != 2) {
      throw new IllegalArgumentException("Not a participant model id: " + modelId);
    } else {
      Conversation c = model.getConversation(parts[0]);
      ParticipantId p = new ParticipantId(parts[1]);
      return Pair.of(c, p);
    }
  }

  private static String[] split(String domId) {
    // Split with -1, rather than 0, to preserve empty strings, which are
    // legitimated model ids (e.g., the root thread of a conversation has an
    // empty id).
    String [] parts = domId.split(SEP, -1);
    for (int i = 0; i < parts.length; i++) {
      parts[i] = CODEC.decode(parts[i]);
    }
    return parts;
  }

  /**
   * Shortens a long id into a short id. If there is already a mapping for
   * {@code longId}, the existing short id is returned. Otherwise, a new short
   * id is generated and returned.
   */
  @VisibleForTesting
  public String shorten(String longId) {
    String shortId = longIdToShortIdMap.get(longId);
    if (shortId == null) {
      shortId = prefix + nextId++;
      registerIdPair(shortId, longId);
    }
    return shortId;
  }

  /**
   * Restores a shortId back to a longId.
   */
  @VisibleForTesting
  public String restoreId(String shortId) {
    return shortIdToLongIdMap.get(shortId);
  }

  /**
   * Registers an id pair.
   *
   * @param shortId short id (arbitrary)
   * @param longId long id (must conform to the grammar for model ids).
   */
  public void registerIdPair(String shortId, String longId) {
    Preconditions.checkArgument(!shortIdToLongIdMap.containsKey(shortId) &&
        !longIdToShortIdMap.containsKey(longId), "Id already exist");
    shortIdToLongIdMap.put(shortId, longId);
    longIdToShortIdMap.put(longId, shortId);
  }

  /**
   * Applies a callback to each pair (shortId, longId) in the mapping.
   *
   * @param callback  callback to invoke on each pair.
   */
  public void forEachPair(ProcV<? super String> callback) {
    shortIdToLongIdMap.each(callback);
  }

  /** @return the counter to use for the next short id. */
  public int getNextId() {
    return nextId;
  }

  /** @return the prefix to prepend on the next short id. */
  public String getPrefix() {
    return prefix;
  }
}
TOP

Related Classes of org.waveprotocol.wave.client.wavepanel.view.ModelIdMapperImpl

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.