**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / System.Runtime.Serialization / ObjectManager.cs
blob9c3652a6a7467e909c590f68a17ef039c1556268
1 //
2 // System.Runtime.Serialization.ObjectManager.cs
3 //
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)
5 //
6 // (C) 2003 Lluis Sanchez Gual
7 //
9 //
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:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
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.
32 using System;
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)
54 _selector = selector;
55 _context = context;
58 public virtual void DoFixups()
60 _finalFixup = true;
62 try
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);
80 ObjectRecord next;
82 if (ready)
84 if (record.OriginalObject is IDeserializationCallback)
85 _deserializedRecords.Add (record);
86 next = record.Next;
88 else
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");
97 else
98 record.Status = ObjectRecordStatus.ReferenceSolvingDelayed;
101 if (record != _lastObjectRecord) {
102 next = record.Next;
103 record.Next = null;
104 _lastObjectRecord.Next = record;
105 _lastObjectRecord = record;
107 else
108 next = record;
111 if (record == last) firstCicle = false;
112 record = next;
115 finally
117 _finalFixup = false;
121 internal ObjectRecord GetObjectRecord (long objectID)
123 ObjectRecord rec = (ObjectRecord)_objectRecords[objectID];
124 if (rec == null)
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");
132 return rec;
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));
164 AddFixup (record);
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));
173 AddFixup (record);
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));
182 AddFixup (record);
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));
191 AddFixup (record);
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");
201 else return;
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;
222 else
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);
243 record.Info = info;
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);
258 record.Info = info;
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.
270 // BaseFixupRecord
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)
285 FixupImpl (manager);
286 return true;
288 else if (strict)
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");
292 else return false;
294 else
295 return false;
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;
307 // ArrayFixupRecord
308 // Fixup for assigning a value to one position of an array
310 internal class ArrayFixupRecord : BaseFixupRecord
312 int _index;
314 public ArrayFixupRecord (ObjectRecord objectToBeFixed, int index, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
315 _index = index;
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
329 int[] _indices;
331 public MultiArrayFixupRecord (ObjectRecord objectToBeFixed, int[] indices, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
332 _indices = indices;
335 protected override void FixupImpl (ObjectManager manager) {
336 ObjectToBeFixed.SetArrayValue (manager, ObjectRequired.ObjectInstance, _indices);
340 // FixupRecord
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) {
348 _member = member;
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);
372 // Object Record
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");
398 if (Member != null)
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))
432 return false;
434 return true;
438 public bool IsUnsolvedObjectReference
440 get {
441 return Status != ObjectRecordStatus.ReferenceSolved;
445 public bool IsRegistered
447 get {
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);
466 else
468 prevFixup = fixup;
469 allFixed = false;
472 fixup = asContainer ? fixup.NextSameContainer : fixup.NextSameRequired;
474 return allFixed;
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);
486 return;
488 prevFixup = fixup;
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;
499 else {
500 if (asContainer) prevFixup.NextSameContainer = fixup.NextSameContainer;
501 else prevFixup.NextSameRequired = fixup.NextSameRequired;
505 public void ChainFixup (BaseFixupRecord fixup, bool asContainer)
507 if (asContainer)
509 fixup.NextSameContainer = FixupChainAsContainer;
510 FixupChainAsContainer = fixup;
512 else
514 fixup.NextSameRequired = FixupChainAsRequired;
515 FixupChainAsRequired = fixup;
519 public bool LoadData (ObjectManager manager, ISurrogateSelector selector, StreamingContext context)
521 if (Info != null)
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);
530 else
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);
538 Info = null;
541 if (ObjectInstance is IObjectReference && Status != ObjectRecordStatus.ReferenceSolved)
543 try {
544 ObjectInstance = ((IObjectReference)ObjectInstance).GetRealObject(context);
545 Status = ObjectRecordStatus.ReferenceSolved;
547 catch (NullReferenceException) {
548 // Give a second chance
549 return false;
553 if (Member != null)
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);
567 return true;
570 public bool HasPendingFixups
572 get { return FixupChainAsContainer != null; }