int main(int argc, char* argv[])
{
  guint users[2];
  InfAdoptedStateVector* vec;
  InfAdoptedStateVector* vec2;

  g_type_init();

  users[0] = 1;
  users[1] = 2;

  /* Note we do not need to allocate users since the state vector does not
   * touch them. */

  vec = inf_adopted_state_vector_new();
  vec2 = inf_adopted_state_vector_new();
  g_assert(inf_adopted_state_vector_causally_before_inc(vec, vec2, 1) == FALSE);
  inf_adopted_state_vector_free(vec2);

  inf_adopted_state_vector_set(vec, users[0], 2);
  g_assert(inf_adopted_state_vector_get(vec, users[0]) == 2);

  inf_adopted_state_vector_add(vec, users[0], 4);
  g_assert(inf_adopted_state_vector_get(vec, users[0]) == 6);

  inf_adopted_state_vector_add(vec, users[1], 3);
  g_assert(inf_adopted_state_vector_get(vec, users[1]) == 3);

  inf_adopted_state_vector_set(vec, users[1], 5);
  g_assert(inf_adopted_state_vector_get(vec, users[1]) == 5);

  inf_adopted_state_vector_free(vec);
  l_test();
  return 0;
}
Exemplo n.º 2
0
/**
 * inf_adopted_request_mirror:
 * @request: A #InfAdoptedRequest.
 * @by: The number of requests between the original and the mirrored
 * operation.
 *
 * Mirrors @request as described in "Reducing the Problems of Group Undo" by
 * Matthias Ressel and Rul Gunzenhäuser
 * (http://portal.acm.org/citation.cfm?doid=320297.320312).
 *
 * Note that @by is the total amount of requests between the original and
 * mirrored request, and thus equivalent to 2j-1 in the paper's definition.
 *
 * @request must be of type %INF_ADOPTED_REQUEST_DO and its operation must
 * be reversible.
 *
 * Returns: The mirrored request as a new #InfAdoptedRequest.
 **/
InfAdoptedRequest*
inf_adopted_request_mirror(InfAdoptedRequest* request,
                           guint by)
{
    InfAdoptedRequestPrivate* priv;
    InfAdoptedOperation* new_operation;
    InfAdoptedStateVector* new_vector;
    InfAdoptedRequest* new_request;

    g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL);
    g_return_val_if_fail(by % 2 == 1, NULL);

    priv = INF_ADOPTED_REQUEST_PRIVATE(request);
    g_return_val_if_fail(priv->type == INF_ADOPTED_REQUEST_DO, NULL);
    g_return_val_if_fail(
        inf_adopted_operation_is_reversible(priv->operation),
        NULL
    );

    new_operation = inf_adopted_operation_revert(priv->operation);
    new_vector = inf_adopted_state_vector_copy(priv->vector);
    inf_adopted_state_vector_add(new_vector, priv->user_id, by);

    new_request = inf_adopted_request_new_do(
                      new_vector,
                      priv->user_id,
                      new_operation
                  );

    g_object_unref(new_operation);
    inf_adopted_state_vector_free(new_vector);
    return new_request;
}
Exemplo n.º 3
0
/* Breadcasts a request N times - makes only sense for undo and redo requests,
 * so that's the only thing we offer API for. */
static void
inf_adopted_session_broadcast_n_requests(InfAdoptedSession* session,
                                         InfAdoptedRequest* request,
                                         guint n)
{
  InfAdoptedSessionPrivate* priv;
  InfAdoptedSessionClass* session_class;
  InfUserTable* user_table;
  guint user_id;
  InfUser* user;
  InfAdoptedSessionLocalUser* local;
  xmlNodePtr xml;

  priv = INF_ADOPTED_SESSION_PRIVATE(session);
  session_class = INF_ADOPTED_SESSION_GET_CLASS(session);
  g_assert(session_class->request_to_xml != NULL);

  user_table = inf_session_get_user_table(INF_SESSION(session));
  user_id = inf_adopted_request_get_user_id(request);
  user = inf_user_table_lookup_user_by_id(user_table, user_id);
  g_assert(user != NULL);

  local = inf_adopted_session_lookup_local_user(
    session,
    INF_ADOPTED_USER(user)
  );
  g_assert(local != NULL);

  xml = xmlNewNode(NULL, (const xmlChar*)"request");

  session_class->request_to_xml(
    session,
    xml,
    request,
    local->last_send_vector,
    FALSE
  );

  if(n > 1) inf_xml_util_set_attribute_uint(xml, "num", n);
  inf_session_send_to_subscriptions(INF_SESSION(session), xml);

  inf_adopted_state_vector_free(local->last_send_vector);
  local->last_send_vector = inf_adopted_state_vector_copy(
    inf_adopted_request_get_vector(request)
  );

  /* Add this request to last send vector if it increases vector time
   * (-> affects buffer). */
  if(inf_adopted_request_affects_buffer(request) == TRUE)
    inf_adopted_state_vector_add(local->last_send_vector, user_id, n);

  inf_adopted_session_stop_noop_timer(session, local);
}
Exemplo n.º 4
0
/**
 * inf_adopted_request_fold:
 * @request: A #InfAdoptedRequest.
 * @into: The direction into which to fold.
 * @by: The number of operations between the original and the fold request.
 *
 * Folds @request as described in "Reducing the Problems of Group Undo" by
 * Matthias Ressel and Rul Gunzenhäuser
 * (http://portal.acm.org/citation.cfm?doid=320297.320312).
 *
 * Note that @by is the total amount of requests between the original and
 * the fold request, and thus equivalent to 2j in the paper's definition.
 *
 * @into must not be the same user as the one that issued @request.
 *
 * Returns: The folded request as a new #InfAdoptedRequest.
 **/
InfAdoptedRequest*
inf_adopted_request_fold(InfAdoptedRequest* request,
                         guint into,
                         guint by)
{
    InfAdoptedRequestPrivate* priv;
    InfAdoptedStateVector* new_vector;
    InfAdoptedRequest* new_request;

    g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL);
    g_return_val_if_fail(into != 0, NULL);
    g_return_val_if_fail(by % 2 == 0, NULL);

    priv = INF_ADOPTED_REQUEST_PRIVATE(request);
    g_return_val_if_fail(priv->user_id != into, NULL);

    new_vector = inf_adopted_state_vector_copy(priv->vector);
    inf_adopted_state_vector_add(new_vector, into, by);

    if(priv->type == INF_ADOPTED_REQUEST_DO)
    {
        new_request = INF_ADOPTED_REQUEST(
                          g_object_new(
                              INF_ADOPTED_TYPE_REQUEST,
                              "type", priv->type,
                              "operation", priv->operation,
                              "vector", new_vector,
                              "user-id", priv->user_id,
                              NULL
                          )
                      );
    }
    else
    {
        new_request = INF_ADOPTED_REQUEST(
                          g_object_new(
                              INF_ADOPTED_TYPE_REQUEST,
                              "type", priv->type,
                              "vector", new_vector,
                              "user-id", priv->user_id
                          )
                      );
    }

    inf_adopted_state_vector_free(new_vector);
    return new_request;
}
Exemplo n.º 5
0
static InfCommunicationScope
inf_adopted_session_process_xml_run(InfSession* session,
                                    InfXmlConnection* connection,
                                    const xmlNodePtr xml,
                                    GError** error)
{
  InfAdoptedSessionPrivate* priv;
  InfAdoptedSessionClass* session_class;
  InfAdoptedRequest* request;
  InfAdoptedUser* user;

  gboolean has_num;
  guint num;
  GError* local_error;
  InfAdoptedRequest* copy_req;
  guint i;

  priv = INF_ADOPTED_SESSION_PRIVATE(session);

  if(strcmp((const char*)xml->name, "request") == 0)
  {
    session_class = INF_ADOPTED_SESSION_GET_CLASS(session);
    g_assert(session_class->xml_to_request != NULL);

    user = inf_adopted_session_user_from_request_xml(
      INF_ADOPTED_SESSION(session),
      xml,
      error
    );

    if(user == NULL)
      return INF_COMMUNICATION_SCOPE_PTP;

    if(inf_user_get_status(INF_USER(user)) == INF_USER_UNAVAILABLE ||
       inf_user_get_connection(INF_USER(user)) != connection)
    {
      g_set_error(
        error,
        inf_user_error_quark(),
        INF_USER_ERROR_NOT_JOINED,
        "%s",
        _("User did not join from this connection")
      );

      return INF_COMMUNICATION_SCOPE_PTP;
    }

    local_error = NULL;
    has_num = inf_xml_util_get_attribute_uint(xml, "num", &num, &local_error);
    if(local_error != NULL)
    {
      g_propagate_error(error, local_error);
      return INF_COMMUNICATION_SCOPE_PTP;
    }
    
    request = session_class->xml_to_request(
      INF_ADOPTED_SESSION(session),
      xml,
      inf_adopted_user_get_vector(user),
      FALSE,
      error
    );

    if(request == NULL)
      return INF_COMMUNICATION_SCOPE_PTP;

    inf_adopted_algorithm_receive_request(priv->algorithm, request);

    /* Apply the request more than once if num is given. This is mostly used
     * for multiple undos and redos, but is in general allowed for any
     * request. */
    if(has_num)
    {
      for(i = 1; i < num; ++i)
      {
        /* TODO: This is a bit of a hack since requests are normally
         * immutable. It avoids an additional vector copy here though. */
        copy_req = inf_adopted_request_copy(request);

        inf_adopted_state_vector_add(
          inf_adopted_request_get_vector(copy_req),
          inf_user_get_id(INF_USER(user)),
          i
        );

        inf_adopted_algorithm_receive_request(priv->algorithm, copy_req);
        g_object_unref(copy_req);
      }
    }

    g_object_unref(request);

    /* Requests can always be forwarded since user is given. */
    return INF_COMMUNICATION_SCOPE_GROUP;
  }

  return INF_SESSION_CLASS(parent_class)->process_xml_run(
    session,
    connection,
    xml,
    error
  );
}
Exemplo n.º 6
0
/**
 * inf_adopted_request_transform:
 * @request: The request to transform.
 * @against: The request to transform against.
 * @concurrency_id: A concurrency ID for the transformation.
 *
 * Transforms the operation of @request against the operation of @against.
 * Both requests must be of type %INF_ADOPTED_REQUEST_DO, and their state
 * vectors must be the same.
 *
 * @concurrency_id can be %INF_ADOPTED_CONCURRENCY_NONE even if the
 * transformation requires a concurrency ID (see
 * inf_adopted_request_need_concurrency_id()). In that case, it is assumed
 * that it does not matter which operation to transform, and user IDs are
 * used to determine a concurrency ID for the transformation.
 *
 * Returns: A new #InfAdoptedRequest, the result of the transformation.
 **/
InfAdoptedRequest*
inf_adopted_request_transform(InfAdoptedRequest* request,
                              InfAdoptedRequest* against,
                              InfAdoptedConcurrencyId concurrency_id)
{
    InfAdoptedRequestPrivate* request_priv;
    InfAdoptedRequestPrivate* against_priv;
    InfAdoptedOperation* new_operation;
    InfAdoptedStateVector* new_vector;
    InfAdoptedRequest* new_request;

    g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL);
    g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(against), NULL);

    request_priv = INF_ADOPTED_REQUEST_PRIVATE(request);
    against_priv = INF_ADOPTED_REQUEST_PRIVATE(against);

    g_return_val_if_fail(request_priv->type == INF_ADOPTED_REQUEST_DO, NULL);
    g_return_val_if_fail(against_priv->type == INF_ADOPTED_REQUEST_DO, NULL);
    g_return_val_if_fail(request_priv->user_id != against_priv->user_id, NULL);

    g_return_val_if_fail(
        inf_adopted_state_vector_compare(
            request_priv->vector,
            against_priv->vector
        ) == 0, NULL
    );

    if(concurrency_id != INF_ADOPTED_CONCURRENCY_NONE)
    {
        new_operation = inf_adopted_operation_transform(
                            request_priv->operation,
                            against_priv->operation,
                            concurrency_id
                        );
    }
    else if(request_priv->user_id > against_priv->user_id)
    {
        new_operation = inf_adopted_operation_transform(
                            request_priv->operation,
                            against_priv->operation,
                            INF_ADOPTED_CONCURRENCY_OTHER
                        );
    }
    else
    {
        new_operation = inf_adopted_operation_transform(
                            request_priv->operation,
                            against_priv->operation,
                            INF_ADOPTED_CONCURRENCY_SELF
                        );
    }

    new_vector = inf_adopted_state_vector_copy(request_priv->vector);
    inf_adopted_state_vector_add(new_vector, against_priv->user_id, 1);

    new_request = inf_adopted_request_new_do(
                      new_vector,
                      request_priv->user_id,
                      new_operation
                  );

    g_object_unref(new_operation);
    inf_adopted_state_vector_free(new_vector);
    return new_request;
}