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> GetRelationship(MemberRelationship source)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> SetRelationship(MemberRelationship source, MemberRelationship relationship)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> SupportsRelationship(MemberRelationship source, MemberRelationship relationship)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 RelationshipEntrySystem.ComponentModel.Design.Serialization.MemberRelationshipService.RelationshipEntry130 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 EqualsSystem.ComponentModel.Design.Serialization.MemberRelationshipService.RelationshipEntry137 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 operator ==System.ComponentModel.Design.Serialization.MemberRelationshipService.RelationshipEntry146 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 operator !=System.ComponentModel.Design.Serialization.MemberRelationshipService.RelationshipEntry152 public static bool operator!=(RelationshipEntry re1, RelationshipEntry re2){ 153 return !(re1 == re2); 154 } 155 GetHashCodeSystem.ComponentModel.Design.Serialization.MemberRelationshipService.RelationshipEntry156 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> MemberRelationshipSystem.ComponentModel.Design.Serialization.MemberRelationship174 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> EqualsSystem.ComponentModel.Design.Serialization.MemberRelationship212 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> GetHashCodeSystem.ComponentModel.Design.Serialization.MemberRelationship223 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> operator ==System.ComponentModel.Design.Serialization.MemberRelationship230 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> operator !=System.ComponentModel.Design.Serialization.MemberRelationship237 public static bool operator !=(MemberRelationship left, MemberRelationship right) { 238 return !(left == right); 239 } 240 } 241 } 242