2 // System.Runtime.Serialization.ObjectManager.cs
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)
6 // (C) 2003 Lluis Sanchez Gual
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System
.Collections
;
34 using System
.Reflection
;
36 namespace System
.Runtime
.Serialization
38 public class ObjectManager
40 // All objects are chained in the same order as they have been registered
41 ObjectRecord _objectRecordChain
= null;
42 ObjectRecord _lastObjectRecord
= null;
44 ArrayList _deserializedRecords
= new ArrayList();
45 Hashtable _objectRecords
= new Hashtable();
46 bool _finalFixup
= false;
48 ISurrogateSelector _selector
;
49 StreamingContext _context
;
50 int _registeredObjectsCount
= 0;
52 public ObjectManager(ISurrogateSelector selector
, StreamingContext context
)
58 public virtual void DoFixups()
64 if (_registeredObjectsCount
< _objectRecords
.Count
)
65 throw new SerializationException ("There are some fixups that refer to objects that have not been registered");
68 ObjectRecord last
= _lastObjectRecord
;
69 bool firstCicle
= true;
71 // Solve al pending fixups of all objects
73 ObjectRecord record
= _objectRecordChain
;
74 while (record
!= null)
76 bool ready
= !(record
.IsUnsolvedObjectReference
&& firstCicle
);
77 if (ready
) ready
= record
.DoFixups (true, this, true);
78 if (ready
) ready
= record
.LoadData(this, _selector
, _context
);
84 if (record
.OriginalObject
is IDeserializationCallback
)
85 _deserializedRecords
.Add (record
);
90 // There must be an unresolved IObjectReference instance.
91 // Chain the record at the end so it is solved later
93 if ((record
.ObjectInstance
is IObjectReference
) && !firstCicle
)
95 if (record
.Status
== ObjectRecordStatus
.ReferenceSolvingDelayed
)
96 throw new SerializationException ("The object with ID " + record
.ObjectID
+ " could not be resolved");
98 record
.Status
= ObjectRecordStatus
.ReferenceSolvingDelayed
;
101 if (record
!= _lastObjectRecord
) {
104 _lastObjectRecord
.Next
= record
;
105 _lastObjectRecord
= record
;
111 if (record
== last
) firstCicle
= false;
121 internal ObjectRecord
GetObjectRecord (long objectID
)
123 ObjectRecord rec
= (ObjectRecord
)_objectRecords
[objectID
];
126 if (_finalFixup
) throw new SerializationException ("The object with Id " + objectID
+ " has not been registered");
127 rec
= new ObjectRecord();
128 rec
.ObjectID
= objectID
;
129 _objectRecords
[objectID
] = rec
;
131 if (!rec
.IsRegistered
&& _finalFixup
) throw new SerializationException ("The object with Id " + objectID
+ " has not been registered");
135 public virtual object GetObject (long objectID
)
137 if (objectID
<= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
138 ObjectRecord rec
= (ObjectRecord
)_objectRecords
[objectID
];
139 if (rec
== null || !rec
.IsRegistered
) return null;
140 else return rec
.ObjectInstance
;
143 public virtual void RaiseDeserializationEvent ()
145 for (int i
= _deserializedRecords
.Count
-1; i
>= 0; i
--)
147 ObjectRecord record
= (ObjectRecord
) _deserializedRecords
[i
];
148 IDeserializationCallback obj
= record
.OriginalObject
as IDeserializationCallback
;
149 if (obj
!= null) obj
.OnDeserialization (this);
153 private void AddFixup (BaseFixupRecord record
)
155 record
.ObjectToBeFixed
.ChainFixup (record
, true);
156 record
.ObjectRequired
.ChainFixup (record
, false);
159 public virtual void RecordArrayElementFixup (long arrayToBeFixed
, int index
, long objectRequired
)
161 if (arrayToBeFixed
<= 0) throw new ArgumentOutOfRangeException("arrayToBeFixed","The arrayToBeFixed parameter is less than or equal to zero");
162 if (objectRequired
<= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
163 ArrayFixupRecord record
= new ArrayFixupRecord(GetObjectRecord(arrayToBeFixed
), index
, GetObjectRecord(objectRequired
));
167 public virtual void RecordArrayElementFixup (long arrayToBeFixed
, int[] indices
, long objectRequired
)
169 if (arrayToBeFixed
<= 0) throw new ArgumentOutOfRangeException("arrayToBeFixed","The arrayToBeFixed parameter is less than or equal to zero");
170 if (objectRequired
<= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
171 if (indices
== null) throw new ArgumentNullException("indices");
172 MultiArrayFixupRecord record
= new MultiArrayFixupRecord (GetObjectRecord(arrayToBeFixed
), indices
, GetObjectRecord(objectRequired
));
176 public virtual void RecordDelayedFixup (long objectToBeFixed
, string memberName
, long objectRequired
)
178 if (objectToBeFixed
<= 0) throw new ArgumentOutOfRangeException("objectToBeFixed","The objectToBeFixed parameter is less than or equal to zero");
179 if (objectRequired
<= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
180 if (memberName
== null) throw new ArgumentNullException("memberName");
181 DelayedFixupRecord record
= new DelayedFixupRecord (GetObjectRecord(objectToBeFixed
), memberName
, GetObjectRecord(objectRequired
));
185 public virtual void RecordFixup (long objectToBeFixed
, MemberInfo member
, long objectRequired
)
187 if (objectToBeFixed
<= 0) throw new ArgumentOutOfRangeException("objectToBeFixed","The objectToBeFixed parameter is less than or equal to zero");
188 if (objectRequired
<= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
189 if (member
== null) throw new ArgumentNullException("member");
190 FixupRecord record
= new FixupRecord (GetObjectRecord(objectToBeFixed
), member
, GetObjectRecord(objectRequired
));
194 private void RegisterObjectInternal (object obj
, ObjectRecord record
)
196 if (obj
== null) throw new ArgumentNullException("obj");
198 if (record
.IsRegistered
)
200 if (record
.OriginalObject
!= obj
) throw new SerializationException ("An object with Id " + record
.ObjectID
+ " has already been registered");
204 record
.ObjectInstance
= obj
;
205 record
.OriginalObject
= obj
;
207 if (obj
is IObjectReference
) record
.Status
= ObjectRecordStatus
.ReferenceUnsolved
;
208 else record
.Status
= ObjectRecordStatus
.ReferenceSolved
;
210 record
.DoFixups (true, this, false);
211 record
.DoFixups (false, this, false);
212 _registeredObjectsCount
++;
214 // Adds the object to the chain of registered objects. This chain
215 // is needed to be able to to perform the final fixups in the right order
217 if (_objectRecordChain
== null)
219 _objectRecordChain
= record
;
220 _lastObjectRecord
= record
;
224 _lastObjectRecord
.Next
= record
;
225 _lastObjectRecord
= record
;
230 public virtual void RegisterObject (object obj
, long objectID
)
232 if (obj
== null) throw new ArgumentNullException("obj", "The obj parameter is null.");
233 if (objectID
<= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
234 RegisterObjectInternal (obj
, GetObjectRecord (objectID
));
237 public void RegisterObject (object obj
, long objectID
, SerializationInfo info
)
239 if (obj
== null) throw new ArgumentNullException("obj", "The obj parameter is null.");
240 if (objectID
<= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
242 ObjectRecord record
= GetObjectRecord (objectID
);
244 RegisterObjectInternal (obj
, record
);
247 public void RegisterObject (object obj
, long objectID
, SerializationInfo info
, long idOfContainingObj
, MemberInfo member
)
249 RegisterObject (obj
, objectID
, info
, idOfContainingObj
, member
, null);
252 public void RegisterObject( object obj
, long objectID
, SerializationInfo info
, long idOfContainingObj
, MemberInfo member
, int[] arrayIndex
)
254 if (obj
== null) throw new ArgumentNullException("obj", "The obj parameter is null.");
255 if (objectID
<= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
257 ObjectRecord record
= GetObjectRecord (objectID
);
259 record
.IdOfContainingObj
= idOfContainingObj
;
260 record
.Member
= member
;
261 record
.ArrayIndex
= arrayIndex
;
262 RegisterObjectInternal (obj
, record
);
268 // Fixup types. There is a fixup class for each fixup type.
271 // Base class for all fixups
273 internal abstract class BaseFixupRecord
275 public BaseFixupRecord(ObjectRecord objectToBeFixed
, ObjectRecord objectRequired
)
277 ObjectToBeFixed
= objectToBeFixed
;
278 ObjectRequired
= objectRequired
;
281 public bool DoFixup (ObjectManager manager
, bool strict
)
283 if (ObjectToBeFixed
.IsRegistered
&& ObjectRequired
.IsInstanceReady
)
290 if (!ObjectToBeFixed
.IsRegistered
) throw new SerializationException ("An object with ID " + ObjectToBeFixed
.ObjectID
+ " was included in a fixup, but it has not been registered");
291 else if (!ObjectRequired
.IsRegistered
) throw new SerializationException ("An object with ID " + ObjectRequired
.ObjectID
+ " was included in a fixup, but it has not been registered");
298 protected abstract void FixupImpl (ObjectManager manager
);
300 internal protected ObjectRecord ObjectToBeFixed
;
301 internal protected ObjectRecord ObjectRequired
;
303 public BaseFixupRecord NextSameContainer
;
304 public BaseFixupRecord NextSameRequired
;
308 // Fixup for assigning a value to one position of an array
310 internal class ArrayFixupRecord
: BaseFixupRecord
314 public ArrayFixupRecord (ObjectRecord objectToBeFixed
, int index
, ObjectRecord objectRequired
): base (objectToBeFixed
, objectRequired
) {
318 protected override void FixupImpl (ObjectManager manager
) {
319 Array array
= (Array
)ObjectToBeFixed
.ObjectInstance
;
320 array
.SetValue (ObjectRequired
.ObjectInstance
, _index
);
324 // MultiArrayFixupRecord
325 // Fixup for assigning a value to several positions of an array
327 internal class MultiArrayFixupRecord
: BaseFixupRecord
331 public MultiArrayFixupRecord (ObjectRecord objectToBeFixed
, int[] indices
, ObjectRecord objectRequired
): base (objectToBeFixed
, objectRequired
) {
335 protected override void FixupImpl (ObjectManager manager
) {
336 ObjectToBeFixed
.SetArrayValue (manager
, ObjectRequired
.ObjectInstance
, _indices
);
341 // Fixup for assigning a value to a member of an object
343 internal class FixupRecord
: BaseFixupRecord
345 public MemberInfo _member
;
347 public FixupRecord (ObjectRecord objectToBeFixed
, MemberInfo member
, ObjectRecord objectRequired
): base (objectToBeFixed
, objectRequired
) {
351 protected override void FixupImpl (ObjectManager manager
) {
352 ObjectToBeFixed
.SetMemberValue (manager
, _member
, ObjectRequired
.ObjectInstance
);
356 // DelayedFixupRecord
357 // Fixup for assigning a value to a SerializationInfo of an object
359 internal class DelayedFixupRecord
: BaseFixupRecord
361 public string _memberName
;
363 public DelayedFixupRecord (ObjectRecord objectToBeFixed
, string memberName
, ObjectRecord objectRequired
): base (objectToBeFixed
, objectRequired
) {
364 _memberName
= memberName
;
367 protected override void FixupImpl (ObjectManager manager
) {
368 ObjectToBeFixed
.SetMemberValue (manager
, _memberName
, ObjectRequired
.ObjectInstance
);
374 internal enum ObjectRecordStatus
: byte { Unregistered, ReferenceUnsolved, ReferenceSolvingDelayed, ReferenceSolved }
376 internal class ObjectRecord
378 public ObjectRecordStatus Status
= ObjectRecordStatus
.Unregistered
;
379 public object OriginalObject
;
380 public object ObjectInstance
;
381 public long ObjectID
;
382 public SerializationInfo Info
;
383 public long IdOfContainingObj
;
384 public MemberInfo Member
;
385 public int[] ArrayIndex
;
386 public BaseFixupRecord FixupChainAsContainer
;
387 public BaseFixupRecord FixupChainAsRequired
;
388 public ObjectRecord Next
;
390 public void SetMemberValue (ObjectManager manager
, MemberInfo member
, object value)
392 if (member
is FieldInfo
)
393 ((FieldInfo
)member
).SetValue (ObjectInstance
, value);
394 else if (member
is PropertyInfo
)
395 ((PropertyInfo
)member
).SetValue (ObjectInstance
, value, null);
396 else throw new SerializationException ("Cannot perform fixup");
400 ObjectRecord containerRecord
= manager
.GetObjectRecord (IdOfContainingObj
);
401 if (containerRecord
.IsRegistered
)
402 containerRecord
.SetMemberValue (manager
, Member
, ObjectInstance
);
404 else if (ArrayIndex
!= null)
406 ObjectRecord containerRecord
= manager
.GetObjectRecord (IdOfContainingObj
);
407 if (containerRecord
.IsRegistered
)
408 containerRecord
.SetArrayValue (manager
, ObjectInstance
, ArrayIndex
);
411 public void SetArrayValue (ObjectManager manager
, object value, int[] indices
)
413 ((Array
)ObjectInstance
).SetValue (value, indices
);
416 public void SetMemberValue (ObjectManager manager
, string memberName
, object value)
418 if (Info
== null) throw new SerializationException ("Cannot perform fixup");
419 Info
.AddValue (memberName
, value, value.GetType());
422 public bool IsInstanceReady
424 // Returns true if this object is ready to be assigned to a parent object.
427 if (!IsRegistered
) return false;
428 if (IsUnsolvedObjectReference
) return false;
430 // Embedded value objects cannot be assigned to their containers until fully completed
431 if (Member
!= null && (HasPendingFixups
|| Info
!= null))
438 public bool IsUnsolvedObjectReference
441 return Status
!= ObjectRecordStatus
.ReferenceSolved
;
445 public bool IsRegistered
448 return Status
!= ObjectRecordStatus
.Unregistered
;
452 public bool DoFixups (bool asContainer
, ObjectManager manager
, bool strict
)
454 BaseFixupRecord prevFixup
= null;
455 BaseFixupRecord fixup
= asContainer
? FixupChainAsContainer
: FixupChainAsRequired
;
456 bool allFixed
= true;
458 while (fixup
!= null)
460 if (fixup
.DoFixup (manager
, strict
))
462 UnchainFixup (fixup
, prevFixup
, asContainer
);
463 if (asContainer
) fixup
.ObjectRequired
.RemoveFixup (fixup
, false);
464 else fixup
.ObjectToBeFixed
.RemoveFixup (fixup
, true);
472 fixup
= asContainer
? fixup
.NextSameContainer
: fixup
.NextSameRequired
;
477 public void RemoveFixup (BaseFixupRecord fixupToRemove
, bool asContainer
)
479 BaseFixupRecord prevFixup
= null;
480 BaseFixupRecord fixup
= asContainer
? FixupChainAsContainer
: FixupChainAsRequired
;
481 while (fixup
!= null)
483 if (fixup
== fixupToRemove
)
485 UnchainFixup (fixup
, prevFixup
, asContainer
);
489 fixup
= asContainer
? fixup
.NextSameContainer
: fixup
.NextSameRequired
;
493 private void UnchainFixup (BaseFixupRecord fixup
, BaseFixupRecord prevFixup
, bool asContainer
)
495 if (prevFixup
== null) {
496 if (asContainer
) FixupChainAsContainer
= fixup
.NextSameContainer
;
497 else FixupChainAsRequired
= fixup
.NextSameRequired
;
500 if (asContainer
) prevFixup
.NextSameContainer
= fixup
.NextSameContainer
;
501 else prevFixup
.NextSameRequired
= fixup
.NextSameRequired
;
505 public void ChainFixup (BaseFixupRecord fixup
, bool asContainer
)
509 fixup
.NextSameContainer
= FixupChainAsContainer
;
510 FixupChainAsContainer
= fixup
;
514 fixup
.NextSameRequired
= FixupChainAsRequired
;
515 FixupChainAsRequired
= fixup
;
519 public bool LoadData (ObjectManager manager
, ISurrogateSelector selector
, StreamingContext context
)
523 if (ObjectInstance
is ISerializable
)
525 object[] pars
= new object[] {Info, context}
;
526 ConstructorInfo con
= ObjectInstance
.GetType().GetConstructor (BindingFlags
.Instance
| BindingFlags
.Public
| BindingFlags
.NonPublic
, null, new Type
[] { typeof (SerializationInfo), typeof (StreamingContext) }
, null );
527 if (con
== null) throw new SerializationException ("The constructor to deserialize an object of type " + ObjectInstance
.GetType().FullName
+ " was not found.");
528 con
.Invoke (ObjectInstance
, pars
);
532 ISurrogateSelector foundSelector
;
533 ISerializationSurrogate surrogate
= selector
.GetSurrogate (ObjectInstance
.GetType(), context
, out foundSelector
);
534 if (surrogate
== null) throw new SerializationException ("No surrogate selector was found for type " + ObjectInstance
.GetType().FullName
);
535 surrogate
.SetObjectData (ObjectInstance
, Info
, context
, foundSelector
);
541 if (ObjectInstance
is IObjectReference
&& Status
!= ObjectRecordStatus
.ReferenceSolved
)
544 ObjectInstance
= ((IObjectReference
)ObjectInstance
).GetRealObject(context
);
545 Status
= ObjectRecordStatus
.ReferenceSolved
;
547 catch (NullReferenceException
) {
548 // Give a second chance
555 // If this object is a value object embedded in another object, the parent
556 // object must be updated
558 ObjectRecord containerRecord
= manager
.GetObjectRecord (IdOfContainingObj
);
559 containerRecord
.SetMemberValue (manager
, Member
, ObjectInstance
);
561 else if (ArrayIndex
!= null)
563 ObjectRecord containerRecord
= manager
.GetObjectRecord (IdOfContainingObj
);
564 containerRecord
.SetArrayValue (manager
, ObjectInstance
, ArrayIndex
);
570 public bool HasPendingFixups
572 get { return FixupChainAsContainer != null; }