Esempio n. 1
0
 public CollectionOperation(CollectionOperation other)
 {
     this.Action = other.Action;
     this.ObjectId = other.ObjectId;
     this.ObjectIndex = other.ObjectIndex;
     this.ClientId = other.ClientId;
     this.ApplyOperation = other.ApplyOperation;
 }
Esempio n. 2
0
 private static void RemoveInsertTransform(ref CollectionOperation remove, ref CollectionOperation insert)
 {
     if (insert.ObjectIndex <= remove.ObjectIndex)
     {
         // For example, locally insert at a lower or equal index (ex: 1) than the incoming delete (ex: 3), we bump the incoming index up (3->4)
         remove.ObjectIndex++;
     }
     else
     {
         // For example, locally delete at a lower index (ex: 1) than the incoming insert (ex: 2), we bump the incoming index down (3->2)
         insert.ObjectIndex--;
     }
 }
Esempio n. 3
0
 private static void RemoveRemoveTransform(ref CollectionOperation pending, ref CollectionOperation incoming)
 {
     // If we have a pending remove and receive a remove on same index, convert both to no-op
     if (pending.ObjectIndex == incoming.ObjectIndex)
     {
         Debug.Assert(pending.ObjectId == incoming.ObjectId);
         pending.ApplyOperation  = false;
         incoming.ApplyOperation = false;
     }
     if (pending.ObjectIndex < incoming.ObjectIndex)
     {
         // For example, locally delete an object at a lower index (ex: 1) than the incoming delete (ex: 3), we bump the incoming index down (3->2)
         incoming.ObjectIndex--;
     }
     else if (pending.ObjectIndex > incoming.ObjectIndex)
     {
         // For example, locally delete an object at a higher index (ex: 4) than the incoming delete (ex: 1), we bump the local (pending) index down (4->3)
         pending.ObjectIndex--;
     }
 }
Esempio n. 4
0
        /// <summary>
        /// Applies operational transform to a pair of operations
        /// </summary>
        /// <param name="localOperation">Action which has already been applied locally</param>
        /// <param name="incomingOperation">Action to be applied</param>
        /// <param name="transformedLocalOperation">Transformed version of localOperation</param>
        public static void ApplyTransform(CollectionOperation localOperation, ref CollectionOperation incomingOperation, out CollectionOperation transformedLocalOperation)
        {
            transformedLocalOperation = new CollectionOperation(localOperation);
            // If pending operation is already a no-op, break
            if ((!transformedLocalOperation.ApplyOperation) || (!incomingOperation.ApplyOperation))
            {
                return;
            }
            switch (transformedLocalOperation.Action)
            {
            case OperationAction.Insert:
            {
                switch (incomingOperation.Action)
                {
                // Insert + Insert
                case OperationAction.Insert:
                {
                    InsertInsertTransform(ref transformedLocalOperation, ref incomingOperation);
                    break;
                }

                // Insert + Remove
                case OperationAction.Remove:
                {
                    InsertRemoveTransform(ref transformedLocalOperation, ref incomingOperation);
                    break;
                }
                }
                break;
            }

            case OperationAction.Remove:
            {
                switch (incomingOperation.Action)
                {
                // Remove + Insert
                case OperationAction.Insert:
                {
                    RemoveInsertTransform(ref transformedLocalOperation, ref incomingOperation);
                    break;
                }

                // Remove + Remove
                case OperationAction.Remove:
                {
                    RemoveRemoveTransform(ref transformedLocalOperation, ref incomingOperation);
                    break;
                }
                }
                break;
            }
            }
            return;
        }
Esempio n. 5
0
        private static void RemoveRemoveTransform(ref CollectionOperation pending, ref CollectionOperation incoming)
        {
            // If we have a pending remove and receive a remove on same index, convert both to no-op
            if (pending.ObjectIndex == incoming.ObjectIndex)
            {
                Debug.Assert(pending.ObjectId == incoming.ObjectId);
                pending.ApplyOperation = false;
                incoming.ApplyOperation = false;
            }
            if (pending.ObjectIndex < incoming.ObjectIndex)
            {
                // For example, locally delete an object at a lower index (ex: 1) than the incoming delete (ex: 3), we bump the incoming index down (3->2)
                incoming.ObjectIndex--;
            }
            else if (pending.ObjectIndex > incoming.ObjectIndex)
            {

                // For example, locally delete an object at a higher index (ex: 4) than the incoming delete (ex: 1), we bump the local (pending) index down (4->3)
                pending.ObjectIndex--;
            }
        }
Esempio n. 6
0
 private static void RemoveInsertTransform(ref CollectionOperation remove, ref CollectionOperation insert)
 {
     if (insert.ObjectIndex <= remove.ObjectIndex)
     {
         // For example, locally insert at a lower or equal index (ex: 1) than the incoming delete (ex: 3), we bump the incoming index up (3->4)
         remove.ObjectIndex++;
     }
     else
     {
         // For example, locally delete at a lower index (ex: 1) than the incoming insert (ex: 2), we bump the incoming index down (3->2)
         insert.ObjectIndex--;
     }
 }
Esempio n. 7
0
 private static void InsertInsertTransform(ref CollectionOperation pending, ref CollectionOperation incoming)
 {
     if (pending.ObjectIndex == incoming.ObjectIndex)
     {
         // If we have a pending insert and receive an insert on same object ID, convert both to no-op
         // For example if two clients attempted to insert the identical object at the same index
         if (pending.ObjectId == incoming.ObjectId)
         {
             pending.ApplyOperation = false;
             incoming.ApplyOperation = false;
         }
         else
         {
             // Use hashcode of ID as tiebreaker
             if (pending.ObjectId.GetHashCode() > incoming.ObjectId.GetHashCode())
             {
                 pending.ObjectIndex++;
             }
             else
             {
                 incoming.ObjectIndex++;
             }
         }
     }
     else if (pending.ObjectIndex < incoming.ObjectIndex)
     {
         // For example, locally insert an object at a lower index (ex: 1) than the incoming insert (ex: 3), we bump the incoming index up (3->4)
         incoming.ObjectIndex++;
     }
     else if (pending.ObjectIndex > incoming.ObjectIndex)
     {
         // For example, locally insert an object at a higher index (ex: 4) than the incoming insert (ex: 1), we bump the local (pending) index up (4->5)
         pending.ObjectIndex++;
     }
 }
Esempio n. 8
0
 /// <summary>
 /// Applies operational transform to a pair of operations
 /// </summary>
 /// <param name="localOperation">Action which has already been applied locally</param>
 /// <param name="incomingOperation">Action to be applied</param>
 /// <param name="transformedLocalOperation">Transformed version of localOperation</param>
 public static void ApplyTransform(CollectionOperation localOperation, ref CollectionOperation incomingOperation, out CollectionOperation transformedLocalOperation)
 {
     transformedLocalOperation = new CollectionOperation(localOperation);
     // If pending operation is already a no-op, break
     if ((!transformedLocalOperation.ApplyOperation) || (!incomingOperation.ApplyOperation))
     {
         return;
     }
     switch (transformedLocalOperation.Action)
     {
         case OperationAction.Insert:
             {
                 switch (incomingOperation.Action)
                 {
                     // Insert + Insert
                     case OperationAction.Insert:
                         {
                             InsertInsertTransform(ref transformedLocalOperation, ref incomingOperation);
                             break;
                         }
                     // Insert + Remove
                     case OperationAction.Remove:
                         {
                             InsertRemoveTransform(ref transformedLocalOperation, ref incomingOperation);
                             break;
                         }
                 }
                 break;
             }
         case OperationAction.Remove:
             {
                 switch (incomingOperation.Action)
                 {
                     // Remove + Insert
                     case OperationAction.Insert:
                         {
                             RemoveInsertTransform(ref transformedLocalOperation, ref incomingOperation);
                             break;
                         }
                     // Remove + Remove
                     case OperationAction.Remove:
                         {
                             RemoveRemoveTransform(ref transformedLocalOperation, ref incomingOperation);
                             break;
                         }
                 }
                 break;
             }
     }
     return;
 }