Merge pull request #2971 from BrzVlad/feature-cross-binprot
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / OrderToken.cs
1 namespace System.Activities.Presentation
2 {
3
4     using System;
5     using System.Collections.Generic;
6     using System.Text;
7     using System.Activities.Presentation;
8
9     /// <summary>
10     /// OrderToken is a generic class used to identify the sort
11     /// order of hierarchical items.  OrderTokens can be used
12     /// to define priority that is based on some predefined defaults or
13     /// based on other OrderTokens.
14     /// </summary>
15     abstract class OrderToken : IComparable<OrderToken>
16     {
17
18         private readonly OrderToken _reference;
19         private readonly OrderTokenPrecedence _precedence;
20         private readonly OrderTokenConflictResolution _conflictResolution;
21
22         private readonly int _depth;
23         private readonly int _index;
24         private int _nextChildIndex;
25
26         /// <summary>
27         /// Creates a new OrderToken instance based on the specified
28         /// referenced OrderToken, precedence, and conflict resolution
29         /// semantics.
30         /// </summary>
31         /// <param name="precedence">Precedence of this token based on the
32         /// referenced token.</param>
33         /// <param name="reference">Referenced token.  May be null for the
34         /// root token case (token that's not dependent on anything else).</param>
35         /// <param name="conflictResolution">Conflict resolution semantics.
36         /// Winning ConflictResultion semantic should only be used
37         /// on predefined, default OrderToken instances to ensure
38         /// their correct placement in more complex chain of order
39         /// dependencies.</param>
40         protected OrderToken(
41             OrderTokenPrecedence precedence,
42             OrderToken reference,
43             OrderTokenConflictResolution conflictResolution)
44         {
45
46             if (!EnumValidator.IsValid(precedence)) throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("precedence"));
47             if (!EnumValidator.IsValid(conflictResolution)) throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("conflictResolution"));
48
49             _reference = reference;
50             _precedence = precedence;
51             _conflictResolution = conflictResolution;
52             _depth = reference == null ? 0 : reference._depth + 1;
53             _index = reference == null ? -1 : reference._nextChildIndex++;
54         }
55
56         /// <summary>
57         /// Compares this order token with the specified order token.
58         /// The comparsion for OrderTokens that don't belong to the same
59         /// chain of OrderTokens will be resolved non-deterministically.
60         /// </summary>
61         /// <param name="other">The token to compare this token to.</param>
62         /// <returns>0 when the tokens have an equal order priority,
63         /// -1 if this order comes before the specified order,
64         /// 1 otherwise.</returns>
65         /// <exception cref="ArgumentNullException">When other is null</exception>
66         public virtual int CompareTo(OrderToken other)
67         {
68
69             if (other == null)
70                 throw FxTrace.Exception.ArgumentNull("other");
71
72             if (other == this)
73                 return 0;
74
75             OrderToken thisOrder = this;
76
77             // Find a common parent
78             while (thisOrder._reference != other._reference)
79             {
80
81                 if (thisOrder._depth == other._depth)
82                 {
83                     thisOrder = thisOrder._reference;
84                     other = other._reference;
85                 }
86                 else
87                 {
88                     if (thisOrder._depth > other._depth)
89                     {
90                         if (thisOrder._reference == other) return thisOrder._precedence == OrderTokenPrecedence.After ? 1 : -1;
91                         thisOrder = thisOrder._reference;
92                     }
93                     else
94                     {
95                         if (other._reference == thisOrder) return other._precedence == OrderTokenPrecedence.After ? -1 : 1;
96                         other = other._reference;
97                     }
98                 }
99             }
100
101             // One order "before", one order "after"?  Easy, return the
102             // "before" order.
103             if (thisOrder._precedence != other._precedence)
104                 return thisOrder._precedence == OrderTokenPrecedence.Before ? -1 : 1;
105
106             // Both orders "before" the parent?  Roots win, otherwise call ResolveConflict().
107             if (thisOrder._precedence == OrderTokenPrecedence.Before)
108             {
109                 if (thisOrder._conflictResolution == OrderTokenConflictResolution.Win)
110                     return -1;
111                 else if (other._conflictResolution == OrderTokenConflictResolution.Win)
112                     return 1;
113                 return ResolveConflict(thisOrder, other);
114             }
115
116             // Both orders "after" the parent?  Roots win, otherwise call ResolveConflict().
117             if (thisOrder._conflictResolution == OrderTokenConflictResolution.Win)
118                 return 1;
119             else if (other._conflictResolution == OrderTokenConflictResolution.Win)
120                 return -1;
121             return ResolveConflict(thisOrder, other);
122         }
123
124         /// <summary>
125         /// This method is called by CompareTo()'s default implementation when two OrderTokens
126         /// appear to be equivalent.  The base functionality of this method uses the instantiation
127         /// order of the two tokens as a tie-breaker.  Override this method to
128         /// implement custom algorithms.  Note that if this method ever returns 0
129         /// (indicating that the two tokens are equivalent) and if these tokens
130         /// belong to a list that gets sorted multiple times, the relative order in
131         /// which they appear in the list will not be guaranteed.  This side-effect
132         /// may or may not be a problem depending on the application.
133         /// </summary>
134         /// <param name="left">Left OrderToken</param>
135         /// <param name="right">Right OrderToken</param>
136         /// <returns>0, if the two are equal, -1, if left comes before right, 
137         /// 1 otherwise.</returns>
138         protected virtual int ResolveConflict(OrderToken left, OrderToken right)
139         {
140             return left._index.CompareTo(right._index);
141         }
142     }
143 }