2 // System.Runtime.Serialization.ObjectManager.cs
\r
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)
\r
6 // (C) 2003 Lluis Sanchez Gual
\r
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
\r
12 // Permission is hereby granted, free of charge, to any person obtaining
\r
13 // a copy of this software and associated documentation files (the
\r
14 // "Software"), to deal in the Software without restriction, including
\r
15 // without limitation the rights to use, copy, modify, merge, publish,
\r
16 // distribute, sublicense, and/or sell copies of the Software, and to
\r
17 // permit persons to whom the Software is furnished to do so, subject to
\r
18 // the following conditions:
\r
20 // The above copyright notice and this permission notice shall be
\r
21 // included in all copies or substantial portions of the Software.
\r
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
33 using System.Collections;
\r
34 using System.Reflection;
\r
36 namespace System.Runtime.Serialization
\r
38 public class ObjectManager
\r
40 // All objects are chained in the same order as they have been registered
\r
41 ObjectRecord _objectRecordChain = null;
\r
42 ObjectRecord _lastObjectRecord = null;
\r
44 ArrayList _deserializedRecords = new ArrayList();
\r
45 Hashtable _objectRecords = new Hashtable();
\r
46 bool _finalFixup = false;
\r
48 ISurrogateSelector _selector;
\r
49 StreamingContext _context;
\r
50 int _registeredObjectsCount = 0;
\r
52 public ObjectManager(ISurrogateSelector selector, StreamingContext context)
\r
54 _selector = selector;
\r
58 public virtual void DoFixups()
\r
64 if (_registeredObjectsCount < _objectRecords.Count)
\r
65 throw new SerializationException ("There are some fixups that refer to objects that have not been registered");
\r
68 ObjectRecord last = _lastObjectRecord;
\r
69 bool firstCicle = true;
\r
71 // Solve al pending fixups of all objects
\r
73 ObjectRecord record = _objectRecordChain;
\r
74 while (record != null)
\r
76 bool ready = !(record.IsUnsolvedObjectReference && firstCicle);
\r
77 if (ready) ready = record.DoFixups (true, this, true);
\r
78 if (ready) ready = record.LoadData(this, _selector, _context);
\r
84 if (record.OriginalObject is IDeserializationCallback)
\r
85 _deserializedRecords.Add (record);
\r
90 // There must be an unresolved IObjectReference instance.
\r
91 // Chain the record at the end so it is solved later
\r
93 if ((record.ObjectInstance is IObjectReference) && !firstCicle)
\r
95 if (record.Status == ObjectRecordStatus.ReferenceSolvingDelayed)
\r
96 throw new SerializationException ("The object with ID " + record.ObjectID + " could not be resolved");
\r
98 record.Status = ObjectRecordStatus.ReferenceSolvingDelayed;
\r
101 if (record != _lastObjectRecord) {
\r
102 next = record.Next;
\r
103 record.Next = null;
\r
104 _lastObjectRecord.Next = record;
\r
105 _lastObjectRecord = record;
\r
111 if (record == last) firstCicle = false;
\r
117 _finalFixup = false;
\r
121 internal ObjectRecord GetObjectRecord (long objectID)
\r
123 ObjectRecord rec = (ObjectRecord)_objectRecords[objectID];
\r
126 if (_finalFixup) throw new SerializationException ("The object with Id " + objectID + " has not been registered");
\r
127 rec = new ObjectRecord();
\r
128 rec.ObjectID = objectID;
\r
129 _objectRecords[objectID] = rec;
\r
131 if (!rec.IsRegistered && _finalFixup) throw new SerializationException ("The object with Id " + objectID + " has not been registered");
\r
135 public virtual object GetObject (long objectID)
\r
137 if (objectID <= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
\r
138 ObjectRecord rec = (ObjectRecord)_objectRecords[objectID];
\r
139 if (rec == null || !rec.IsRegistered) return null;
\r
140 else return rec.ObjectInstance;
\r
143 public virtual void RaiseDeserializationEvent ()
\r
145 for (int i = _deserializedRecords.Count-1; i >= 0; i--)
\r
147 ObjectRecord record = (ObjectRecord) _deserializedRecords [i];
\r
148 IDeserializationCallback obj = record.OriginalObject as IDeserializationCallback;
\r
149 if (obj != null) obj.OnDeserialization (this);
\r
153 private void AddFixup (BaseFixupRecord record)
\r
155 record.ObjectToBeFixed.ChainFixup (record, true);
\r
156 record.ObjectRequired.ChainFixup (record, false);
\r
159 public virtual void RecordArrayElementFixup (long arrayToBeFixed, int index, long objectRequired)
\r
161 if (arrayToBeFixed <= 0) throw new ArgumentOutOfRangeException("arrayToBeFixed","The arrayToBeFixed parameter is less than or equal to zero");
\r
162 if (objectRequired <= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
\r
163 ArrayFixupRecord record = new ArrayFixupRecord(GetObjectRecord(arrayToBeFixed), index, GetObjectRecord(objectRequired));
\r
167 public virtual void RecordArrayElementFixup (long arrayToBeFixed, int[] indices, long objectRequired)
\r
169 if (arrayToBeFixed <= 0) throw new ArgumentOutOfRangeException("arrayToBeFixed","The arrayToBeFixed parameter is less than or equal to zero");
\r
170 if (objectRequired <= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
\r
171 if (indices == null) throw new ArgumentNullException("indices");
\r
172 MultiArrayFixupRecord record = new MultiArrayFixupRecord (GetObjectRecord(arrayToBeFixed), indices, GetObjectRecord(objectRequired));
\r
176 public virtual void RecordDelayedFixup (long objectToBeFixed, string memberName, long objectRequired)
\r
178 if (objectToBeFixed <= 0) throw new ArgumentOutOfRangeException("objectToBeFixed","The objectToBeFixed parameter is less than or equal to zero");
\r
179 if (objectRequired <= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
\r
180 if (memberName == null) throw new ArgumentNullException("memberName");
\r
181 DelayedFixupRecord record = new DelayedFixupRecord (GetObjectRecord(objectToBeFixed), memberName, GetObjectRecord(objectRequired));
\r
185 public virtual void RecordFixup (long objectToBeFixed, MemberInfo member, long objectRequired)
\r
187 if (objectToBeFixed <= 0) throw new ArgumentOutOfRangeException("objectToBeFixed","The objectToBeFixed parameter is less than or equal to zero");
\r
188 if (objectRequired <= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
\r
189 if (member == null) throw new ArgumentNullException("member");
\r
190 FixupRecord record = new FixupRecord (GetObjectRecord(objectToBeFixed), member, GetObjectRecord(objectRequired));
\r
194 private void RegisterObjectInternal (object obj, ObjectRecord record)
\r
196 if (obj == null) throw new ArgumentNullException("obj");
\r
198 if (record.IsRegistered)
\r
200 if (record.OriginalObject != obj) throw new SerializationException ("An object with Id " + record.ObjectID + " has already been registered");
\r
204 record.ObjectInstance = obj;
\r
205 record.OriginalObject = obj;
\r
207 if (obj is IObjectReference) record.Status = ObjectRecordStatus.ReferenceUnsolved;
\r
208 else record.Status = ObjectRecordStatus.ReferenceSolved;
\r
210 if (_selector != null) {
\r
211 record.Surrogate = _selector.GetSurrogate (
\r
212 obj.GetType(), _context, out record.SurrogateSelector);
\r
213 if (record.Surrogate != null)
\r
214 record.Status = ObjectRecordStatus.ReferenceUnsolved;
\r
217 record.DoFixups (true, this, false);
\r
218 record.DoFixups (false, this, false);
\r
219 _registeredObjectsCount++;
\r
221 // Adds the object to the chain of registered objects. This chain
\r
222 // is needed to be able to to perform the final fixups in the right order
\r
224 if (_objectRecordChain == null)
\r
226 _objectRecordChain = record;
\r
227 _lastObjectRecord = record;
\r
231 _lastObjectRecord.Next = record;
\r
232 _lastObjectRecord = record;
\r
237 public virtual void RegisterObject (object obj, long objectID)
\r
239 if (obj == null) throw new ArgumentNullException("obj", "The obj parameter is null.");
\r
240 if (objectID <= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
\r
241 RegisterObjectInternal (obj, GetObjectRecord (objectID));
\r
244 public void RegisterObject (object obj, long objectID, SerializationInfo info)
\r
246 if (obj == null) throw new ArgumentNullException("obj", "The obj parameter is null.");
\r
247 if (objectID <= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
\r
249 ObjectRecord record = GetObjectRecord (objectID);
\r
250 record.Info = info;
\r
251 RegisterObjectInternal (obj, record);
\r
254 public void RegisterObject (object obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member)
\r
256 RegisterObject (obj, objectID, info, idOfContainingObj, member, null);
\r
259 public void RegisterObject( object obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member, int[] arrayIndex)
\r
261 if (obj == null) throw new ArgumentNullException("obj", "The obj parameter is null.");
\r
262 if (objectID <= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
\r
264 ObjectRecord record = GetObjectRecord (objectID);
\r
265 record.Info = info;
\r
266 record.IdOfContainingObj = idOfContainingObj;
\r
267 record.Member = member;
\r
268 record.ArrayIndex = arrayIndex;
\r
269 RegisterObjectInternal (obj, record);
\r
275 // Fixup types. There is a fixup class for each fixup type.
\r
278 // Base class for all fixups
\r
280 internal abstract class BaseFixupRecord
\r
282 public BaseFixupRecord(ObjectRecord objectToBeFixed, ObjectRecord objectRequired)
\r
284 ObjectToBeFixed = objectToBeFixed;
\r
285 ObjectRequired = objectRequired;
\r
288 public bool DoFixup (ObjectManager manager, bool strict)
\r
290 if (ObjectToBeFixed.IsRegistered && ObjectRequired.IsInstanceReady)
\r
292 FixupImpl (manager);
\r
297 if (!ObjectToBeFixed.IsRegistered) throw new SerializationException ("An object with ID " + ObjectToBeFixed.ObjectID + " was included in a fixup, but it has not been registered");
\r
298 else if (!ObjectRequired.IsRegistered) throw new SerializationException ("An object with ID " + ObjectRequired.ObjectID + " was included in a fixup, but it has not been registered");
\r
305 protected abstract void FixupImpl (ObjectManager manager);
\r
307 internal protected ObjectRecord ObjectToBeFixed;
\r
308 internal protected ObjectRecord ObjectRequired;
\r
310 public BaseFixupRecord NextSameContainer;
\r
311 public BaseFixupRecord NextSameRequired;
\r
314 // ArrayFixupRecord
\r
315 // Fixup for assigning a value to one position of an array
\r
317 internal class ArrayFixupRecord : BaseFixupRecord
\r
321 public ArrayFixupRecord (ObjectRecord objectToBeFixed, int index, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
\r
325 protected override void FixupImpl (ObjectManager manager) {
\r
326 Array array = (Array)ObjectToBeFixed.ObjectInstance;
\r
327 array.SetValue (ObjectRequired.ObjectInstance, _index);
\r
331 // MultiArrayFixupRecord
\r
332 // Fixup for assigning a value to several positions of an array
\r
334 internal class MultiArrayFixupRecord : BaseFixupRecord
\r
338 public MultiArrayFixupRecord (ObjectRecord objectToBeFixed, int[] indices, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
\r
339 _indices = indices;
\r
342 protected override void FixupImpl (ObjectManager manager) {
\r
343 ObjectToBeFixed.SetArrayValue (manager, ObjectRequired.ObjectInstance, _indices);
\r
348 // Fixup for assigning a value to a member of an object
\r
350 internal class FixupRecord: BaseFixupRecord
\r
352 public MemberInfo _member;
\r
354 public FixupRecord (ObjectRecord objectToBeFixed, MemberInfo member, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
\r
358 protected override void FixupImpl (ObjectManager manager) {
\r
359 ObjectToBeFixed.SetMemberValue (manager, _member, ObjectRequired.ObjectInstance);
\r
363 // DelayedFixupRecord
\r
364 // Fixup for assigning a value to a SerializationInfo of an object
\r
366 internal class DelayedFixupRecord: BaseFixupRecord
\r
368 public string _memberName;
\r
370 public DelayedFixupRecord (ObjectRecord objectToBeFixed, string memberName, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
\r
371 _memberName = memberName;
\r
374 protected override void FixupImpl (ObjectManager manager) {
\r
375 ObjectToBeFixed.SetMemberValue (manager, _memberName, ObjectRequired.ObjectInstance);
\r
381 internal enum ObjectRecordStatus: byte { Unregistered, ReferenceUnsolved, ReferenceSolvingDelayed, ReferenceSolved }
\r
383 internal class ObjectRecord
\r
385 public ObjectRecordStatus Status = ObjectRecordStatus.Unregistered;
\r
386 public object OriginalObject;
\r
387 public object ObjectInstance;
\r
388 public long ObjectID;
\r
389 public SerializationInfo Info;
\r
390 public long IdOfContainingObj;
\r
391 public ISerializationSurrogate Surrogate;
\r
392 public ISurrogateSelector SurrogateSelector;
\r
393 public MemberInfo Member;
\r
394 public int[] ArrayIndex;
\r
395 public BaseFixupRecord FixupChainAsContainer;
\r
396 public BaseFixupRecord FixupChainAsRequired;
\r
397 public ObjectRecord Next;
\r
399 public void SetMemberValue (ObjectManager manager, MemberInfo member, object value)
\r
401 if (member is FieldInfo)
\r
402 ((FieldInfo)member).SetValue (ObjectInstance, value);
\r
403 else if (member is PropertyInfo)
\r
404 ((PropertyInfo)member).SetValue (ObjectInstance, value, null);
\r
405 else throw new SerializationException ("Cannot perform fixup");
\r
407 if (Member != null)
\r
409 ObjectRecord containerRecord = manager.GetObjectRecord (IdOfContainingObj);
\r
410 if (containerRecord.IsRegistered)
\r
411 containerRecord.SetMemberValue (manager, Member, ObjectInstance);
\r
413 else if (ArrayIndex != null)
\r
415 ObjectRecord containerRecord = manager.GetObjectRecord (IdOfContainingObj);
\r
416 if (containerRecord.IsRegistered)
\r
417 containerRecord.SetArrayValue (manager, ObjectInstance, ArrayIndex);
\r
420 public void SetArrayValue (ObjectManager manager, object value, int[] indices)
\r
422 ((Array)ObjectInstance).SetValue (value, indices);
\r
425 public void SetMemberValue (ObjectManager manager, string memberName, object value)
\r
427 if (Info == null) throw new SerializationException ("Cannot perform fixup");
\r
428 Info.AddValue (memberName, value, value.GetType());
\r
431 public bool IsInstanceReady
\r
433 // Returns true if this object is ready to be assigned to a parent object.
\r
436 if (!IsRegistered) return false;
\r
437 if (IsUnsolvedObjectReference) return false;
\r
439 // Embedded value objects cannot be assigned to their containers until fully completed
\r
440 if (ObjectInstance.GetType ().IsValueType && (HasPendingFixups || Info != null))
\r
447 public bool IsUnsolvedObjectReference
\r
450 return Status != ObjectRecordStatus.ReferenceSolved;
\r
454 public bool IsRegistered
\r
457 return Status != ObjectRecordStatus.Unregistered;
\r
461 public bool DoFixups (bool asContainer, ObjectManager manager, bool strict)
\r
463 BaseFixupRecord prevFixup = null;
\r
464 BaseFixupRecord fixup = asContainer ? FixupChainAsContainer : FixupChainAsRequired;
\r
465 bool allFixed = true;
\r
467 while (fixup != null)
\r
469 if (fixup.DoFixup (manager, strict))
\r
471 UnchainFixup (fixup, prevFixup, asContainer);
\r
472 if (asContainer) fixup.ObjectRequired.RemoveFixup (fixup, false);
\r
473 else fixup.ObjectToBeFixed.RemoveFixup (fixup, true);
\r
481 fixup = asContainer ? fixup.NextSameContainer : fixup.NextSameRequired;
\r
486 public void RemoveFixup (BaseFixupRecord fixupToRemove, bool asContainer)
\r
488 BaseFixupRecord prevFixup = null;
\r
489 BaseFixupRecord fixup = asContainer ? FixupChainAsContainer : FixupChainAsRequired;
\r
490 while (fixup != null)
\r
492 if (fixup == fixupToRemove)
\r
494 UnchainFixup (fixup, prevFixup, asContainer);
\r
498 fixup = asContainer ? fixup.NextSameContainer : fixup.NextSameRequired;
\r
502 private void UnchainFixup (BaseFixupRecord fixup, BaseFixupRecord prevFixup, bool asContainer)
\r
504 if (prevFixup == null) {
\r
505 if (asContainer) FixupChainAsContainer = fixup.NextSameContainer;
\r
506 else FixupChainAsRequired = fixup.NextSameRequired;
\r
509 if (asContainer) prevFixup.NextSameContainer = fixup.NextSameContainer;
\r
510 else prevFixup.NextSameRequired = fixup.NextSameRequired;
\r
514 public void ChainFixup (BaseFixupRecord fixup, bool asContainer)
\r
518 fixup.NextSameContainer = FixupChainAsContainer;
\r
519 FixupChainAsContainer = fixup;
\r
523 fixup.NextSameRequired = FixupChainAsRequired;
\r
524 FixupChainAsRequired = fixup;
\r
528 public bool LoadData (ObjectManager manager, ISurrogateSelector selector, StreamingContext context)
\r
532 if (Surrogate != null) {
\r
533 object new_obj = Surrogate.SetObjectData (ObjectInstance, Info, context, SurrogateSelector);
\r
534 if (new_obj != null)
\r
535 ObjectInstance = new_obj;
\r
536 Status = ObjectRecordStatus.ReferenceSolved;
\r
537 } else if (ObjectInstance is ISerializable) {
\r
538 object[] pars = new object[] {Info, context};
\r
539 ConstructorInfo con = ObjectInstance.GetType().GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof (SerializationInfo), typeof (StreamingContext) }, null );
\r
540 if (con == null) throw new SerializationException ("The constructor to deserialize an object of type " + ObjectInstance.GetType().FullName + " was not found.");
\r
541 con.Invoke (ObjectInstance, pars);
\r
543 throw new SerializationException ("No surrogate selector was found for type " + ObjectInstance.GetType().FullName);
\r
549 if (ObjectInstance is IObjectReference && Status != ObjectRecordStatus.ReferenceSolved)
\r
552 ObjectInstance = ((IObjectReference)ObjectInstance).GetRealObject(context);
554 while (ObjectInstance is IObjectReference && n > 0) {
555 object ob = ((IObjectReference)ObjectInstance).GetRealObject (context);
556 if (ob == ObjectInstance)
562 throw new SerializationException ("The implementation of the IObjectReference interface returns too many nested references to other objects that implement IObjectReference.");
564 Status = ObjectRecordStatus.ReferenceSolved;
\r
566 catch (NullReferenceException) {
\r
567 // Give a second chance
\r
572 if (Member != null)
\r
574 // If this object is a value object embedded in another object, the parent
\r
575 // object must be updated
\r
577 ObjectRecord containerRecord = manager.GetObjectRecord (IdOfContainingObj);
\r
578 containerRecord.SetMemberValue (manager, Member, ObjectInstance);
\r
580 else if (ArrayIndex != null)
\r
582 ObjectRecord containerRecord = manager.GetObjectRecord (IdOfContainingObj);
\r
583 containerRecord.SetArrayValue (manager, ObjectInstance, ArrayIndex);
\r
589 public bool HasPendingFixups
\r
591 get { return FixupChainAsContainer != null; }
\r