Project

General

Profile

root / branches / compiler / cSharp / ooasCompiler / src / libs / c5 / UserGuideExamples / LockstepCollections.cs @ 3

1
/*
2
 Copyright (c) 2003-2007 Niels Kokholm and Peter Sestoft
3
 Permission is hereby granted, free of charge, to any person obtaining a copy
4
 of this software and associated documentation files (the "Software"), to deal
5
 in the Software without restriction, including without limitation the rights
6
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
 copies of the Software, and to permit persons to whom the Software is
8
 furnished to do so, subject to the following conditions:
9
 
10
 The above copyright notice and this permission notice shall be included in
11
 all copies or substantial portions of the Software.
12
 
13
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
 SOFTWARE.
20
*/
21

    
22
// C5 example: LockstepCollections: A group collections lockstep, so
23
// that they all contain the same items.  This is done by equipping
24
// them all with event handlers, so that when one collection is
25
// updated (item added or removed) then all the others are updated
26
// too.  The collections must be initially empty.
27

    
28
// The idea is to organize the N collections c0, ..., c(N-1) in a ring
29
// in which c(i) is followed by c((i+1)%N), and then each collection
30
// with event handlers that update the next collection.  The update to
31
// that collection in turn will cause its event listeners to update
32
// the next next collection, and so on.
33

    
34
// Event cycles are prevented by using a common counter "steps",
35
// initialized to the number N of collections, and reset to N by
36
// CollectionChanged events.  All other events decrement the counter
37
// and performing further updates only if it is positive.  Since the
38
// CollectionChanged events are fired only after all the other events,
39
// the counter will be reset only after all the updates have been
40
// performed, but then it will (superfluously) be reset N times.  None
41
// of this will work in a multithreaded setting, of course; but then
42
// even plain updates wouldn't either.
43

    
44
// If the (equality) comparer of one collection identifies more items
45
// than that of another collection, then the lockstep collections do
46
// not necessarily have the same number of items, even though they all
47
// started out empty.  This also means that it matters what collection
48
// one attempts to add an item to, and in what order the collections
49
// are chained.  If one adds a new item to a collection that contains
50
// one deemed equal to it, then nothing happens, the event is not
51
// raised, and the item will not be added to any of the collections.
52
// If instead one adds it to a collection where it is not deemed equal
53
// to an existing item, it will be added to that collection but
54
// possibly not to others.
55

    
56
// Compile with 
57
//   csc /r:C5.dll LockstepCollections.cs 
58

    
59
using System;                           // Console
60
using C5; 
61
using SCG = System.Collections.Generic;
62

    
63
namespace LockstepCollections
64
{
65
  static class LockstepCollections
66
  {
67
    static void Main(String[] args)
68
    {
69
      ICollection<Person> nameColl 
70
        = new HashSet<Person>(new Person.NameEqualityComparer());
71
      ICollection<Person> dateColl 
72
        = new TreeSet<Person>(new Person.DateComparer());
73
      MakeLockstep(nameColl, dateColl);
74
      Person p1 = new Person("Peter", 19620625), 
75
        p2 = new Person("Carsten", 19640627), 
76
        p3 = new Person("Carsten", 19640628);
77
      nameColl.Add(p1);      
78
      nameColl.Add(p2);
79
      dateColl.Add(p3);
80
      Console.WriteLine("dateColl = {0}", dateColl);
81
      Console.WriteLine("nameColl = {0}", nameColl);
82
      dateColl.Remove(p1);
83
      Console.WriteLine("dateColl = {0}", dateColl);
84
      Console.WriteLine("nameColl = {0}", nameColl);
85
      dateColl.Clear();
86
      Console.WriteLine("dateColl = {0}", dateColl);
87
      Console.WriteLine("nameColl = {0}", nameColl);
88
    }
89

    
90
    static void MakeLockstep<T>(params ICollection<T>[] colls)
91
    {
92
      // These will be captured in the event handler delegates below
93
      int N = colls.Length;
94
      int steps = N;
95
      for (int i=0; i<N; i++) {
96
        if (!colls[i].IsEmpty) 
97
          throw new ApplicationException("Non-empty collection");
98
      }
99
      for (int i=0; i<N; i++) {
100
        ICollection<T> thisColl = colls[i];
101
        ICollection<T> nextColl = colls[(i+1)%N];
102
        thisColl.CollectionChanged += 
103
          delegate(Object coll) {
104
            steps = N;
105
          };
106
        thisColl.CollectionCleared += 
107
          delegate(Object coll, ClearedEventArgs args) {
108
            // For now ignoring that the clearing may be partial
109
            if (--steps > 0) {
110
              nextColl.Clear();
111
            }
112
          };
113
        thisColl.ItemsAdded +=
114
          delegate(Object coll, ItemCountEventArgs<T> args) {
115
            // For now ignoring the multiplicity
116
            if (--steps > 0) {
117
              T item = args.Item;
118
              nextColl.FindOrAdd(ref item);
119
            }
120
          };
121
        thisColl.ItemsRemoved +=
122
          delegate(Object coll, ItemCountEventArgs<T> args) {
123
            // For now ignoring the multiplicity
124
            if (--steps > 0) {
125
              nextColl.Remove(args.Item);
126
            }
127
          };
128
      }
129
    }
130
  }
131

    
132
  public class Person {
133
    String name;
134
    int date;
135

    
136
    public Person(String name, int date) {
137
      this.name = name;
138
      this.date = date;
139
    }
140

    
141
    public class NameEqualityComparer : SCG.IEqualityComparer<Person> {
142
      public bool Equals(Person p1, Person p2) {
143
        return p1.name == p2.name;
144
      }
145
      public int GetHashCode(Person p) { 
146
        return p.name.GetHashCode();
147
      }
148
    }
149

    
150
    public class DateComparer : SCG.IComparer<Person> {
151
      public int Compare(Person p1, Person p2) { 
152
        return p1.date.CompareTo(p2.date);
153
      }
154
    }
155

    
156
    public override String ToString() {
157
      return name + " (" + date + ")";
158
    }
159
  }
160
}