Initial commit
[mono.git] / mcs / class / referencesource / System / compmod / system / componentmodel / design / serialization / MemberRelationshipService.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="MemberRelationshipService.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6 namespace System.ComponentModel.Design.Serialization {
7     using System.Collections.Generic;
8     using System.Diagnostics.CodeAnalysis;
9     using System.Security.Permissions;
10
11     /// <devdoc>
12     ///    A member relationship service is used by a serializer to announce that one
13     ///    property is related to a property on another object. Consider a code
14     ///    based serialization scheme where code is of the following form:
15     /// 
16     ///    object1.Property1 = object2.Property2
17     /// 
18     ///    Upon interpretation of this code, Property1 on object1 will be
19     ///    set to the return value of object2.Property2.  But the relationship
20     ///    between these two objects is lost.  Serialization schemes that
21     ///    wish to maintain this relationship may install a MemberRelationshipService
22     ///    into the serialization manager.  When an object is deserialized
23     ///    this serivce will be notified of these relationships.  It is up to the service
24     ///    to act on these notifications if it wishes.  During serialization, the
25     ///    service is also consulted.  If a relationship exists the same
26     ///    relationship is maintained by the serializer.
27     /// </devdoc>
28     [HostProtection(SharedState = true)]
29     public abstract class MemberRelationshipService
30     {
31         private Dictionary<RelationshipEntry,RelationshipEntry> _relationships = new Dictionary<RelationshipEntry,RelationshipEntry>();
32
33         /// <devdoc>
34         ///    Returns the the current relationship associated with the source, or MemberRelationship.Empty if
35         ///    there is no relationship.  Also sets a relationship between two objects.  Empty
36         ///    can also be passed as the property value, in which case the relationship will
37         ///    be cleared.
38         /// </devdoc>
39         [SuppressMessage("Microsoft.Design", "CA1043:UseIntegralOrStringArgumentForIndexers")]
40         public MemberRelationship this[MemberRelationship source] {
41             get {
42                 if (source.Owner == null) throw new ArgumentNullException("Owner");
43                 if (source.Member== null) throw new ArgumentNullException("Member");
44
45                 return GetRelationship(source);
46             }
47             set {
48                 if (source.Owner == null) throw new ArgumentNullException("Owner");
49                 if (source.Member == null) throw new ArgumentNullException("Member");
50
51                 SetRelationship(source, value);
52             }
53         }
54
55         /// <devdoc>
56         ///    Returns the the current relationship associated with the source, or null if
57         ///    there is no relationship.  Also sets a relationship between two objects.  Null
58         ///    can be passed as the property value, in which case the relationship will
59         ///    be cleared.
60         /// </devdoc>
61         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1023:IndexersShouldNotBeMultidimensional")]
62         public MemberRelationship this[object sourceOwner, MemberDescriptor sourceMember] {
63             get {
64                 if (sourceOwner == null) throw new ArgumentNullException("sourceOwner");
65                 if (sourceMember == null) throw new ArgumentNullException("sourceMember");
66
67                 return GetRelationship(new MemberRelationship(sourceOwner, sourceMember));
68             }
69             set {
70                 if (sourceOwner == null) throw new ArgumentNullException("sourceOwner");
71                 if (sourceMember == null) throw new ArgumentNullException("sourceMember");
72
73                 SetRelationship(new MemberRelationship(sourceOwner, sourceMember), value);
74             }
75         }
76
77         /// <devdoc>
78         ///    This is the implementation API for returning relationships.  The default implementation stores the 
79         ///    relationship in a table.  Relationships are stored weakly, so they do not keep an object alive.
80         /// </devdoc>
81         protected virtual MemberRelationship GetRelationship(MemberRelationship source) {
82             RelationshipEntry retVal;
83
84             if (_relationships != null && _relationships.TryGetValue(new RelationshipEntry(source), out retVal) && retVal.Owner.IsAlive) {
85                 return new MemberRelationship(retVal.Owner.Target, retVal.Member);
86             }
87
88             return MemberRelationship.Empty;
89         }
90
91         /// <devdoc>
92         ///    This is the implementation API for returning relationships.  The default implementation stores the 
93         ///    relationship in a table.  Relationships are stored weakly, so they do not keep an object alive.  Empty can be
94         ///    passed in for relationship to remove the relationship.
95         /// </devdoc>
96         protected virtual void SetRelationship(MemberRelationship source, MemberRelationship relationship) {
97
98             if (!relationship.IsEmpty && !SupportsRelationship(source, relationship)) {
99                 string sourceName = TypeDescriptor.GetComponentName(source.Owner);
100                 string relName = TypeDescriptor.GetComponentName(relationship.Owner);
101                 if (sourceName == null) {
102                     sourceName = source.Owner.ToString();
103                 }
104                 if (relName == null) {
105                     relName = relationship.Owner.ToString();
106                 }
107                 throw new ArgumentException(SR.GetString(SR.MemberRelationshipService_RelationshipNotSupported, sourceName, source.Member.Name, relName, relationship.Member.Name));
108             }
109
110             if (_relationships == null) {
111                 _relationships = new Dictionary<RelationshipEntry,RelationshipEntry>();
112             }
113
114             _relationships[new RelationshipEntry(source)] = new RelationshipEntry(relationship);
115         }
116
117         /// <devdoc>
118         ///    Returns true if the provided relatinoship is supported.
119         /// </devdoc>
120         public abstract bool SupportsRelationship(MemberRelationship source, MemberRelationship relationship);
121
122         /// <devdoc>
123         ///    Used as storage in our relationship table
124         /// </devdoc>
125         private struct RelationshipEntry {
126             internal WeakReference Owner;
127             internal MemberDescriptor Member;
128             private int hashCode;
129
130             internal RelationshipEntry(MemberRelationship rel) {
131                 Owner = new WeakReference(rel.Owner);
132                 Member = rel.Member;
133                 hashCode = rel.Owner == null ? 0 : rel.Owner.GetHashCode();
134             }
135
136
137             public override bool Equals(object o) {
138                 if (o is RelationshipEntry) {
139                     RelationshipEntry e = (RelationshipEntry)o;
140                     return this == e;
141                 }
142
143                 return false;
144             }
145
146             public static bool operator==(RelationshipEntry re1, RelationshipEntry re2){
147                 object owner1 = (re1.Owner.IsAlive ? re1.Owner.Target : null);
148                 object owner2 = (re2.Owner.IsAlive ? re2.Owner.Target : null);
149                 return owner1 == owner2 && re1.Member.Equals(re2.Member);
150             }
151
152             public static bool operator!=(RelationshipEntry re1, RelationshipEntry re2){
153                 return !(re1 == re2);
154             }
155
156             public override int GetHashCode() {
157                 return hashCode;
158             }
159         }
160     }
161
162     /// <devdoc>
163     ///    This class represents a single relationship between an object and a member.
164     /// </devdoc>
165     public struct MemberRelationship {
166         private object _owner;
167         private MemberDescriptor _member;
168
169         public static readonly MemberRelationship Empty = new MemberRelationship();
170
171         /// <devdoc>
172         ///    Creates a new member relationship.
173         /// </devdoc>
174         public MemberRelationship(object owner, MemberDescriptor member) {
175             if (owner == null) throw new ArgumentNullException("owner");
176             if (member == null) throw new ArgumentNullException("member");
177
178             _owner = owner;
179             _member = member;
180         }
181
182         /// <devdoc>
183         ///    Returns true if this relationship is empty.
184         /// </devdoc>
185         public bool IsEmpty {
186             get {
187                 return _owner == null;
188             }
189         }
190
191         /// <devdoc>
192         ///    The member in this relationship.
193         /// </devdoc>
194         public MemberDescriptor Member {
195             get {
196                 return _member;
197             }
198         }
199
200         /// <devdoc>
201         ///    The object owning the member.
202         /// </devdoc>
203         public object Owner {
204             get {
205                 return _owner;
206             }
207         }
208
209         /// <devdoc>
210         ///    Infrastructure support to make this a first class struct
211         /// </devdoc>
212         public override bool Equals(object obj) {
213             if (!(obj is MemberRelationship))
214                 return false;
215
216             MemberRelationship rel = (MemberRelationship)obj;
217             return rel.Owner == Owner && rel.Member == Member;
218         }
219
220         /// <devdoc>
221         ///    Infrastructure support to make this a first class struct
222         /// </devdoc>
223         public override int GetHashCode() {
224             if (_owner == null) return base.GetHashCode();
225             return _owner.GetHashCode() ^ _member.GetHashCode();
226         }
227         /// <devdoc>
228         ///    Infrastructure support to make this a first class struct
229         /// </devdoc>
230         public static bool operator ==(MemberRelationship left, MemberRelationship right) {
231             return left.Owner == right.Owner && left.Member == right.Member;
232         }
233
234         /// <devdoc>
235         ///    Infrastructure support to make this a first class struct
236         /// </devdoc>
237         public static bool operator !=(MemberRelationship left, MemberRelationship right) {
238             return !(left == right);
239         }
240     }
241 }