Package com.google.wave.extensions.polly

Source Code of com.google.wave.extensions.polly.PollMetadata

// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.wave.extensions.polly;

import com.google.wave.api.RobotMessageBundle;
import com.google.wave.api.Wavelet;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Represents the persistent state of the poll stored in wave. This class
* provides simple serialization/deserialization into a string. Data documents
* can be arbitrary xml or string data.
*
* @author scovitz@google.com (Seth Covitz)
*/
public class PollMetadata {

  // Delimeter and separator constants.
  private static final String LINE_SEP_REGEX = "\\n";
  private static final String FIELD_SEP_REGEX = "\\:";

  private static final String METADATA_DOC = "poll-metadata";
  private static final String METADATA_FIELD_SEP = "|";
  private static final String METADATA_FIELD_SEP_REGEX = "\\|";
  private static final String METADATA_ID_SEP = "$";
  private static final String METADATA_ID_SEP_REGEX = "\\$";

  private static final String NEWIDS_DOC = "_new_ids_";
  private static final String NEWIDS_POLL_BLIP_PREFIX = "poll-blip-";
 
  private static final String VOTES_DOC = "poll-votes";

  /**
   * The previously stored title for an AdminWavelet or the distributed poll
   * title for a PollWavelet.
   */
  private String title = "";
 
  /**
   * The previously stored question for an AdminWavelet or the distributed poll
   * question for a PollWavelet.
   */
  private String question = "";

  /**
   * The previously stored list of choices for an AdminWavelet or the
   * distributed list of choices for a PollWavelet.
   */
  private List<String> choices = new ArrayList<String>();

  /**
   * The list of poll recipients for the AdminWavelet or the current pollee
   * for the PollWavelet.
   */
  private String recipients = "";
 
  /**
   * A map of recipients to PollWavelets.
   */
  private Map<String, String> pollWaveletBlipIds = new HashMap<String, String>();
 
  /**
   * A map of recipients to their votes.
   */
  private Map<String, String> pollVotes = new HashMap<String, String>();
 
  /**
   * The AdminWavelet Id for PollWavelets to communicate their vote.
   */
  private String resultsWaveletId = "";

  /**
   * The serialized form of the data document to compare against to determine
   * changes.
   */
  private String dataDocument = "";
 
  /**
   * The current robot context.
   */
  private RobotMessageBundle context;
 
  /**
   * Whether any of the poll metadata has changed (and should be saved).
   */
  private boolean hasChanged = true;
 
  /**
   * Whether new votes have been received since the last time the metadata was
   * read.
   */
  private boolean hasNewVotes = false;

  /**
   * Constructs the previous state of the Admin or Poll wavelet from metadata
   * stored in a data document.
   *
   * @param context the context the robot is currently called with.
   */
  public PollMetadata(RobotMessageBundle context) {
    this.context = context;
    loadMetadata();
    processNewIds();
    processNewVotes();
  }

  /**
   * Deserializes the previous state of the poll metadata from the data stored
   * in the data document.
   */
  private void loadMetadata() {
    this.dataDocument = context.getWavelet().getDataDocument(METADATA_DOC);
    if (dataDocument != null) {
      try {
        String[] items = dataDocument.split(METADATA_FIELD_SEP_REGEX);

        this.title = items[0];
        this.question = items[1];
       
        String choicesString = items[2];
        String[] choices = choicesString.split(METADATA_ID_SEP_REGEX);
        this.choices.clear();
        for (String choice : choices) {
          this.choices.add(choice);
        }

        this.recipients = items[3];
        this.resultsWaveletId = items[4];

        String pollWaveletBlipString = items[5];
        String[] pollBlips = pollWaveletBlipString.split(METADATA_ID_SEP_REGEX);
        for (int i = 0; i < pollBlips.length; i += 3) {
          pollWaveletBlipIds.put(pollBlips[i], pollBlips[i + 1]);
          pollVotes.put(pollBlips[i], pollBlips[i + 2]);
        }
      } catch(IndexOutOfBoundsException iobx) {
        // Receiving an exception means that the data document has not yet
        // been fully populated.
      }
      hasChanged = false;
    }
  }
 
  /**
   * Saves the poll metadata as a data document in the robot context's wavelet.
   */
  public void saveMetadata() {
    saveMetadata(context.getWavelet());
  }
 
  /**
   * Serializes the state of the poll metadata to the given wavelet. If the
   * new data document happens to be unchanged (ie. a person voted with the
   * same vote twice), the data document is not saved.
   *
   * @param wavelet the wavelet in which to persist the data document.
   */
  public void saveMetadata(Wavelet wavelet) {
    StringBuilder pollWaveletBlipString = new StringBuilder();
    for (Map.Entry<String, String> pollBlip : pollWaveletBlipIds.entrySet()) {
      pollWaveletBlipString.append(METADATA_ID_SEP);
      pollWaveletBlipString.append(pollBlip.getKey());
      pollWaveletBlipString.append(METADATA_ID_SEP);
      pollWaveletBlipString.append(pollBlip.getValue());
      pollWaveletBlipString.append(METADATA_ID_SEP);
      pollWaveletBlipString.append(pollVotes.get(pollBlip.getKey()));
    }
    if (pollWaveletBlipString.length() > 0) {
      pollWaveletBlipString.deleteCharAt(0);
    }
   
    StringBuilder choicesString = new StringBuilder();
    for (String choice : this.choices) {
      choicesString.append(METADATA_ID_SEP);
      choicesString.append(choice);
    }
    if (choicesString.length() > 0) {
      choicesString.deleteCharAt(0);
    }
   
    StringBuilder newDataDocument = new StringBuilder();
   
    newDataDocument.append(title);
    newDataDocument.append(METADATA_FIELD_SEP);
    newDataDocument.append(question);
    newDataDocument.append(METADATA_FIELD_SEP);
    newDataDocument.append(choicesString);
    newDataDocument.append(METADATA_FIELD_SEP);
    newDataDocument.append(recipients);
    newDataDocument.append(METADATA_FIELD_SEP);
    newDataDocument.append(resultsWaveletId);
    newDataDocument.append(METADATA_FIELD_SEP);
    newDataDocument.append(pollWaveletBlipString);
   
    if (!newDataDocument.toString().equals(dataDocument)) {
      wavelet.setDataDocument(METADATA_DOC, newDataDocument.toString());
    }
  }

  /**
   * Checks for the presence of the _new_ids_ data document and if present,
   * processes and stores the newly created poll wavelet ids.
   */
  private void processNewIds() {
    String newIds = context.getWavelet().getDataDocument(NEWIDS_DOC);
    if (newIds != null && !newIds.isEmpty()) {
      try {
        for (String line : newIds.split(LINE_SEP_REGEX)) {
          if (line.startsWith(NEWIDS_POLL_BLIP_PREFIX)) {
            String[] pollBlip = line.split(FIELD_SEP_REGEX);
            String participant = pollBlip[0].substring(NEWIDS_POLL_BLIP_PREFIX.length());
            String participantPollBlipId = pollBlip[1];
            pollWaveletBlipIds.put(participant, participantPollBlipId);
            hasChanged = true;
          }
        }
      } catch(IndexOutOfBoundsException iobx) {
        // Receiving an exception means that the data document has not yet
        // been populated.
      }
      context.getWavelet().setDataDocument(NEWIDS_DOC, "");
    }
  }

  /**
   * Checks for the presence of the poll-votes data document and if present,
   * process and stores any newly received votes. Only a single vote is stored
   * per pollee so the most recent vote overrides any previous votes.
   */
  private void processNewVotes() {
    String newVotes = context.getWavelet().getDataDocument(VOTES_DOC);
    if (newVotes != null && !newVotes.isEmpty()) {
      try {
        for (String line : newVotes.split(LINE_SEP_REGEX)) {
          String[] vote = line.split(FIELD_SEP_REGEX);
          String participant = vote[0];
          String choice = vote[1];
          pollVotes.put(participant, choice);
          hasChanged = true;
          hasNewVotes = true;
        }
      } catch(IndexOutOfBoundsException iobx) {
        // Receiving an exception means that the data document has not yet
        // been populated.
      }
      context.getWavelet().setDataDocument(VOTES_DOC, "");
    }
  }

  /**
   * Returns whether the metadata has changed. This can be caused by the
   * addition of new polls or votes, or the updating of metadata by changes in
   * the AdminWavelet.
   *
   * @return true if the metadata has changed, false otherwise.
   */
  public boolean hasChanged() {
    return hasChanged;
  }

  /**
   * Returns the poll title.
   *
   * @return the poll title.
   */
  public String getTitle() {
    return title;
  }
 
  /**
   * Returns the poll question.
   *
   * @return the poll question.
   */
  public String getQuestion() {
    return question;
  }

  /**
   * Returns the list of recipients.
   *
   * @return the list of recipients.
   */
  public String getRecipients() {
    return recipients;
  }

  /**
   * Returns the list of choices.
   *
   * @return the list of choices.
   */
  public List<String> getChoices() {
    return choices;
  }

  /**
   * Accumulates the current vote tally for each choice and return it as a
   * list.
   *
   * @return the list of votes for each choice.
   */
  public List<Integer> getChoiceCounts() {
    // Initialize the counts to zero.
    List<Integer> counts = new ArrayList<Integer>();
    for (String choice : choices) {
      counts.add(0);
    }

    // Accumulate non-null votes.
    for (String vote : pollVotes.values()) {
      if (!"null".equals(vote)) {
        int voteIndex = Integer.parseInt(vote);
        counts.set(voteIndex, counts.get(voteIndex) + 1);
      }
    }
   
    return counts;
  }

  /**
   * Converts the comma separated list of recipients into a list.
   *
   * @return a list of recipients.
   */
  public List<String> getRecipientsAsList() {
    List<String> recipientList = new ArrayList<String>();
    if (!recipients.isEmpty()) {
      for (String recipient : recipients.split(",")) {
        recipientList.add(recipient.trim());
      }
    }
    return recipientList;
  }

  /**
   * Returns the list of recipients that have voted.
   *
   * @return the list of recipients that have voted.
   */
  public List<String> getVoters() {
    List<String> voters = new ArrayList<String>();
    for (Map.Entry<String, String> vote : pollVotes.entrySet()) {
      if (!"null".equals(vote.getValue())) {
        voters.add(vote.getKey());
      }
    }
    return voters;
  }

  /**
   * Sets the poll title.
   *
   * @param title the new poll title value.
   */
  public void setTitle(String title) {
    this.title = title;
    hasChanged = true;
  }
 
  /**
   * Sets the poll question.
   *
   * @param question the new poll question value.
   */
  public void setQuestion(String question) {
    this.question = question;
    hasChanged = true;
  }
 
  /**
   * Sets the list of choices.
   *
   * @param choices the new list of choices.
   */
  public void setChoices(List<String> choices) {
    this.choices = choices;
    hasChanged = true;
  }

  /**
   * Sets the list of recipients.
   *
   * @param recipients the new list of recipients.
   */
  public void setRecipients(String recipients) {
    this.recipients = recipients;
    hasChanged = true;
  }

  /**
   * Sets the results wavelet id. This is used mainly by PollWavelets to
   * communicate back their votes. The results wavelet is synonymous with
   * the AdminWavelet.
   *
   * @param resultsWaveletId
   */
  public void setResultsWaveletId(String resultsWaveletId) {
    this.resultsWaveletId = resultsWaveletId;
    hasChanged = true;
  }

  /**
   * Returns the results wavelet id.
   *
   * @return the results wavelet id.
   */
  public String getResultsWaveletId() {
    return resultsWaveletId;
  }

  /**
   * Writes a pollee's vote to the votes data document of the results wavelet.
   *
   * @param vote the choice that the user selected.
   */
  public void writeVoteToResults(String vote) {
    String[] waveIdParts = resultsWaveletId.split(" ");
    String waveId = waveIdParts[0];
    String waveletId = waveIdParts[1];
    Wavelet resultsWavelet = context.getWavelet(waveId, waveletId);
   
    // Append is used rather than set to support concurrent access by pollees
    // voting at the same time.
    resultsWavelet.appendDataDocument(VOTES_DOC,
        recipients + ":" + vote + "\n");
  }

  /**
   * Returns whether new votes have been detected in the metadata. This is
   * useful for updating the results pane display.
   *
   * @return true if there are new votes, false otherwise.
   */
  public boolean hasNewVotes() {
    return hasNewVotes;
  }

  /**
   * Constructs a string to identify the new poll wavelet id for a recipient.
   * Writebacks are necessary as the wavelet isn't created until the operation
   * is received by the RobotProxy server. In order to access a wavelet
   * without an event having been generated, one will need the id. Hence a
   * writeback mechanism is used as a notification of the newly created id once
   * it is available.
   *
   * @param recipient
   * @return the key for the writeback id.
   */
  public String getPollWriteback(String recipient) {
    return NEWIDS_POLL_BLIP_PREFIX + recipient;
  }

  /**
   * Determines whether a given recipient has voted or not.
   *
   * @param recipient the recipient to check.
   * @return true if the recipient has voted, false otherwise.
   */
  public boolean hasVoter(String recipient) {
    return pollVotes.containsKey(recipient);
  }

  /**
   * Adds a recipient to the list of voters. A 'null' vote is added by default.
   *
   * @param recipient the recipient to be added.
   */
  public void addVoter(String recipient) {
    if (!hasVoter(recipient)) {
      pollVotes.put(recipient, "null");
      hasChanged = true;
    }
  }
}
TOP

Related Classes of com.google.wave.extensions.polly.PollMetadata

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.