/**
 * inf_adopted_operation_transform:
 * @operation: The #InfAdoptedOperation to transform.
 * @against: The operation to transform against.
 * @concurrency_id: The concurrency ID for the transformation.
 *
 * Performs an inclusion transformation of @operation against @against,
 * meaning that the effect of @against is included in @operation.
 *
 * If inf_adopted_operation_need_concurrency_id() returns %TRUE for @operation
 * and @against, then @concurrency_id must not be
 * %INF_ADOPTED_CONCURRENCY_NONE. Otherwise, the parameter is ignored.
 *
 * Return Value: The transformed #InfAdoptedOperation, or %NULL if the
 * transformation failed.
 **/
InfAdoptedOperation*
inf_adopted_operation_transform(InfAdoptedOperation* operation,
                                InfAdoptedOperation* against,
                                InfAdoptedConcurrencyId concurrency_id)
{
  InfAdoptedOperationIface* iface;

  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL);
  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(against), NULL);

  /* Transform against both parts of split operation if we are transforming
   * against split operation. */
  if(INF_ADOPTED_IS_SPLIT_OPERATION(against))
  {
    return inf_adopted_split_operation_transform_other(
      INF_ADOPTED_SPLIT_OPERATION(against),
      operation,
      concurrency_id
    );
  }
  else
  {
    iface = INF_ADOPTED_OPERATION_GET_IFACE(operation);
    g_assert(iface->transform != NULL);

    return (*iface->transform)(operation, against, concurrency_id);
  }
}
/**
 * inf_adopted_operation_need_concurrency_id:
 * @operation: The #InfAdoptedOperation to transform.
 * @against: The operation to transform against.
 *
 * This function returns whether transforming @operation against @against
 * is not defined unambiguously. In that case, transformation requires a
 * so-called concurrency ID which determines which of the two operations
 * is transformed.
 *
 * Returns: Whether transformation of @operation against @against requires a
 * concurrency ID to be defined.
 */
gboolean
inf_adopted_operation_need_concurrency_id(InfAdoptedOperation* operation,
                                          InfAdoptedOperation* against)
{
  InfAdoptedOperationIface* iface;

  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), FALSE);
  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(against), FALSE);

  iface = INF_ADOPTED_OPERATION_GET_IFACE(operation);
  g_assert(iface->need_concurrency_id != NULL);

  return iface->need_concurrency_id(operation, against);
}
/**
 * inf_adopted_operation_get_concurrency_id:
 * @operation: The #InfAdoptedOperation to transform.
 * @against: The operation to transform against.
 *
 * This function returns a concurrency ID for transformation of @operation
 * against @against. It always returns %INF_ADOPTED_CONCURRENCY_NONE when
 * inf_adopted_operation_need_concurrency_id() returns %TRUE for
 * @operation and @against (but that's not necessarily true the other way
 * around), since it is not possible to decide which operation to transform
 * without any additional information.
 *
 * However, the function can be called on the same operations in a previous
 * state. In some cases, a decision can be made based on those previous
 * operations. This can then be used as concurrency ID to call
 * inf_adopted_operation_transform().
 *
 * Note that the function is antisymmetric. If it returns
 * %INF_ADOPTED_CONCURRENCY_SELF, then it returns
 * %INF_ADOPTED_CONCURRENCY_OTHER for swapped arguments.
 *
 * Returns: A concurrency ID between @operation and @against. Can be
 * %INF_ADOPTED_CONCURRENCY_NONE in case no decision can be made.
 */
InfAdoptedConcurrencyId
inf_adopted_operation_get_concurrency_id(InfAdoptedOperation* operation,
                                         InfAdoptedOperation* against)
{
  InfAdoptedOperationIface* iface;
  InfAdoptedConcurrencyId id;

  g_return_val_if_fail(
    INF_ADOPTED_IS_OPERATION(operation),
    INF_ADOPTED_CONCURRENCY_NONE
  );
  g_return_val_if_fail(
    INF_ADOPTED_IS_OPERATION(against),
    INF_ADOPTED_CONCURRENCY_NONE
  );

  /* Use antisymmetricity if second argument is split operation, so that
   * subclasses don't need to handle that case explicitely. */
  if(!INF_ADOPTED_IS_SPLIT_OPERATION(operation) &&
     INF_ADOPTED_IS_SPLIT_OPERATION(against))
  {
    iface = INF_ADOPTED_OPERATION_GET_IFACE(against);
    g_assert(iface->get_concurrency_id != NULL);

    id = iface->get_concurrency_id(against, operation);
    switch(id)
    {
    case INF_ADOPTED_CONCURRENCY_SELF:
      return INF_ADOPTED_CONCURRENCY_OTHER;
    case INF_ADOPTED_CONCURRENCY_NONE:
      return INF_ADOPTED_CONCURRENCY_NONE;
    case INF_ADOPTED_CONCURRENCY_OTHER:
      return INF_ADOPTED_CONCURRENCY_SELF;
    default:
      g_assert_not_reached();
      return INF_ADOPTED_CONCURRENCY_NONE;
    }
  }
  else
  {
    iface = INF_ADOPTED_OPERATION_GET_IFACE(operation);
    g_assert(iface->get_concurrency_id != NULL);

    return iface->get_concurrency_id(operation, against);
  }
}
/**
 * inf_text_delete_operation_need_concurrency_id:
 * @op: A #InfTextDeleteOperation.
 * @against: Another #InfAdoptedOperation.
 *
 * Returns whether transforming @op against @against requires a concurrency ID
 * (see inf_adopted_operation_need_concurrency_id() for further information).
 *
 * Returns: Whether transforming @op against @against requires a concurrency
 * ID.
 */
gboolean
inf_text_delete_operation_need_concurrency_id(InfTextDeleteOperation* op,
                                              InfAdoptedOperation* against)
{
  g_return_val_if_fail(INF_TEXT_IS_DELETE_OPERATION(op), FALSE);
  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(against), FALSE);

  return FALSE;
}
/**
 * inf_adopted_operation_make_reversible:
 * @operation: A #InfAdoptedOperation.
 * @with: Another #InfAdoptedOperation that emerged from @operation by
 * transforming it.
 * @buffer: A #InfBuffer.
 *
 * Some operations may not be reversible, but can be made reversible with
 * some extra information such as another operation that collected
 * enough information while being transformed, and the current buffer.
 *
 * This function can only be called when @operation is not yet reversible
 * and returns a new operation that has the same effect as @operation, but is
 * reversible.
 *
 * For example, an operation that deletes some range of text in a text editor
 * is not reversible if it only stores the position and length of the range,
 * but can be made reversible when it looks up what there is at that position
 * in the buffer.
 *
 * Return Value: A reversible #InfAdoptedOperation, or %NULL if @operation
 * cannot be made reversible with the given transformed operation @with and
 * @buffer.
 **/
InfAdoptedOperation*
inf_adopted_operation_make_reversible(InfAdoptedOperation* operation,
                                      InfAdoptedOperation* with,
                                      InfBuffer* buffer)
{
  InfAdoptedOperationIface* iface;

  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL);
  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(with), NULL);
  g_return_val_if_fail(INF_IS_BUFFER(buffer), NULL);

  g_assert(inf_adopted_operation_is_reversible(operation) == FALSE);

  iface = INF_ADOPTED_OPERATION_GET_IFACE(operation);

  if(iface->make_reversible != NULL)
    return (*iface->make_reversible)(operation, with, buffer);
  else
    return NULL;
}
/**
 * inf_adopted_operation_copy:
 * @operation: The #InfAdoptedOperation to copy.
 *
 * Returns a copy of @operation.
 *
 * Return Value: A copy of @operation.
 **/
InfAdoptedOperation*
inf_adopted_operation_copy(InfAdoptedOperation* operation)
{
  InfAdoptedOperationIface* iface;

  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL);

  iface = INF_ADOPTED_OPERATION_GET_IFACE(operation);

  g_assert(iface->copy != NULL);
  return (*iface->copy)(operation);
}
/**
 * inf_adopted_operation_is_reversible:
 * @operation: A #InfAdoptedOperation.
 *
 * Returns whether @operation is reversible.
 *
 * Return Value: Whether @operation is reversible.
 **/
gboolean
inf_adopted_operation_is_reversible(InfAdoptedOperation* operation)
{
  InfAdoptedOperationFlags flags;

  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), FALSE);

  flags = inf_adopted_operation_get_flags(operation);
  if( (flags & INF_ADOPTED_OPERATION_REVERSIBLE) != 0)
    return TRUE;
  else
    return FALSE;
}
/**
 * inf_adopted_operation_get_flags:
 * @operation: A #InfAdoptedOperation.
 *
 * Returns the flags for @operation.
 *
 * Return Value: #InfAdoptedOperationFlags for @operation.
 **/
InfAdoptedOperationFlags
inf_adopted_operation_get_flags(InfAdoptedOperation* operation)
{
  InfAdoptedOperationIface* iface;

  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), 0);

  iface = INF_ADOPTED_OPERATION_GET_IFACE(operation);

  if(iface->get_flags != NULL)
    return (*iface->get_flags)(operation);
  else
    return 0;
}
/**
 * inf_adopted_operation_revert:
 * @operation: A #InfAdoptedOperation.
 *
 * Returns a new #InfAdoptedOperation that undoes the effect of @operation. If
 * @operation and then its reverse operation are applied to a buffer (in that
 * order), the buffer remains unchanged.
 *
 * @operation must be reversible for this function to be called (i.e.
 * inf_adopted_operation_is_reversible() must return TRUE).
 *
 * Return Value: The reverse operation of @operation.
 **/
InfAdoptedOperation*
inf_adopted_operation_revert(InfAdoptedOperation* operation)
{
  InfAdoptedOperationIface* iface;

  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL);
  g_assert(inf_adopted_operation_is_reversible(operation) == TRUE);

  iface = INF_ADOPTED_OPERATION_GET_IFACE(operation);

  /* When inf_adopted_operation_is_reversible() returns TRUE for an operation
   * it must implement revert. */
  g_assert(iface->revert != NULL);
  return (*iface->revert)(operation);
}
/**
 * inf_adopted_operation_apply:
 * @operation: A #InfAdoptedOperation.
 * @by: A #InfAdoptedUser.
 * @buffer: The #InfBuffer to apply the operation to.
 *
 * Applies @operation to @buffer. The operation is considered to be applied by
 * user @by.
 **/
void
inf_adopted_operation_apply(InfAdoptedOperation* operation,
                            InfAdoptedUser* by,
                            InfBuffer* buffer)
{
  InfAdoptedOperationIface* iface;

  g_return_if_fail(INF_ADOPTED_IS_OPERATION(operation));
  g_return_if_fail(INF_ADOPTED_IS_USER(by));
  g_return_if_fail(INF_IS_BUFFER(buffer));

  iface = INF_ADOPTED_OPERATION_GET_IFACE(operation);

  /* apply must be implemented */
  g_assert(iface->apply != NULL);
  (*iface->apply)(operation, by, buffer);
}
/**
 * inf_text_insert_operation_need_concurrency_id:
 * @op: A #InfTextInsertOperation.
 * @against: Another #InfAdoptedOperation.
 *
 * Returns whether transforming @op against @against requires a concurrency ID
 * (see inf_adopted_operation_need_concurrency_id() for further information).
 *
 * Returns: Whether transforming @op against @against requires a concurrency
 * ID.
 */
gboolean
inf_text_insert_operation_need_concurrency_id(InfTextInsertOperation* op,
                                              InfAdoptedOperation* against)
{
  InfTextInsertOperation* insert_against;

  g_return_val_if_fail(INF_TEXT_IS_INSERT_OPERATION(op), FALSE);
  g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(against), FALSE);

  if(INF_TEXT_IS_INSERT_OPERATION(against))
  {
    insert_against = INF_TEXT_INSERT_OPERATION(against);
    if(inf_text_insert_operation_get_position(op) ==
       inf_text_insert_operation_get_position(insert_against))
    {
      return TRUE;
    }
  }

  return FALSE;
}
示例#12
0
/**
 * inf_adopted_request_new_do:
 * @vector: The vector time at which the request was made.
 * @user_id: The ID of the user that made the request.
 * @operation: The operation the user performed.
 *
 * Creates a new #InfAdoptedRequest with type %INF_ADOPTED_REQUEST_DO.
 *
 * Return Value: A new DO request.
 **/
InfAdoptedRequest*
inf_adopted_request_new_do(InfAdoptedStateVector* vector,
                           guint user_id,
                           InfAdoptedOperation* operation)
{
    GObject* object;

    g_return_val_if_fail(vector != NULL, NULL);
    g_return_val_if_fail(user_id != 0, NULL);
    g_return_val_if_fail(INF_ADOPTED_IS_OPERATION(operation), NULL);

    object = g_object_new(
                 INF_ADOPTED_TYPE_REQUEST,
                 "type", INF_ADOPTED_REQUEST_DO,
                 "vector", vector,
                 "user-id", user_id,
                 "operation", operation,
                 NULL
             );

    return INF_ADOPTED_REQUEST(object);
}