[S.R.Serialization] for some use cases we don't expect reflection exception.
[mono.git] / mcs / class / System.Runtime.Serialization / ReferenceSources / JsonFormatReaderGenerator_static.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Reflection;
5 using System.Runtime;
6 using System.Xml;
7
8 namespace System.Runtime.Serialization.Json
9 {
10         internal partial class JsonFormatReaderGenerator
11         {
12                 partial class CriticalHelper
13                 {
14                         internal JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
15                         {
16                                 return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDictionaryString, XmlDictionaryString [] memberNames) => new JsonFormatReaderInterpreter (classContract).ReadFromJson (xr, ctx, emptyDictionaryString, memberNames);
17                         }
18
19                         internal JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
20                         {
21                                 return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDS, XmlDictionaryString inm, CollectionDataContract cc) => new JsonFormatReaderInterpreter (collectionContract, false).ReadCollectionFromJson (xr, ctx, emptyDS, inm, cc);
22                         }
23                         
24                         internal JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
25                         {
26                                 return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDS, XmlDictionaryString inm, CollectionDataContract cc) => new JsonFormatReaderInterpreter (collectionContract, true).ReadGetOnlyCollectionFromJson (xr, ctx, emptyDS, inm, cc);
27                         }
28                 }
29         }
30
31         class JsonFormatReaderInterpreter
32         {
33                 public JsonFormatReaderInterpreter (ClassDataContract classContract)
34                 {
35                         this.classContract = classContract;
36                 }
37
38                 public JsonFormatReaderInterpreter (CollectionDataContract collectionContract, bool isGetOnly)
39                 {
40                         this.collectionContract = collectionContract;
41                         this.is_get_only_collection = isGetOnly;
42                 }
43
44                 bool is_get_only_collection;
45
46                 ClassDataContract classContract;
47
48                 CollectionDataContract collectionContract;
49
50                 object objectLocal;
51                 Type objectType;
52                 XmlReaderDelegator xmlReader;
53                 XmlObjectSerializerReadContextComplexJson context;
54
55                 XmlDictionaryString [] memberNames = null;
56                 XmlDictionaryString emptyDictionaryString = null;
57                 XmlDictionaryString itemName = null;
58                 XmlDictionaryString itemNamespace = null;
59
60                 public object ReadFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString[] memberNames)
61                 {
62                         // InitArgs()
63                         this.xmlReader = xmlReader;
64                         this.context = context;
65                         this.emptyDictionaryString = emptyDictionaryString;
66                         this.memberNames = memberNames;
67                         
68                         //DemandSerializationFormatterPermission(classContract);
69                         //DemandMemberAccessPermission(memberAccessFlag);
70                         CreateObject (classContract);
71                         
72                         context.AddNewObject (objectLocal);
73                         InvokeOnDeserializing (classContract);
74             
75             string objectId = null;
76             
77                         if (classContract.IsISerializable)
78                                 ReadISerializable (classContract);
79                         else
80                                 ReadClass (classContract);
81                         if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom (classContract.UnderlyingType))
82                                 ((IDeserializationCallback) objectLocal).OnDeserialization (null);
83                         InvokeOnDeserialized(classContract);
84                         if (!InvokeFactoryMethod (classContract)) {
85
86                                 // Do a conversion back from DateTimeOffsetAdapter to DateTimeOffset after deserialization.
87                                 // DateTimeOffsetAdapter is used here for deserialization purposes to bypass the ISerializable implementation
88                                 // on DateTimeOffset; which does not work in partial trust.
89
90                                 if (classContract.UnderlyingType == Globals.TypeOfDateTimeOffsetAdapter)
91                                         objectLocal = DateTimeOffsetAdapter.GetDateTimeOffset ((DateTimeOffsetAdapter) objectLocal);
92                                 // else - do we have to call CodeInterpreter.ConvertValue()? I guess not...
93                         }
94                         return objectLocal;
95                 }
96                 
97                 public object ReadCollectionFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract)
98                 {
99                         #region GenerateCollectionReaderHelper
100                         // InitArgs()
101                         this.xmlReader = xmlReader;
102                         this.context = context;
103                         this.emptyDictionaryString = emptyDictionaryString;
104                         this.itemName = itemName;
105
106                         this.collectionContract = collectionContract;
107
108                         #endregion
109
110                         ReadCollection (collectionContract);
111
112                         return objectLocal;
113                 }
114                 
115                 public void ReadGetOnlyCollectionFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract)
116                 {
117                         #region GenerateCollectionReaderHelper
118                         // InitArgs()
119                         this.xmlReader = xmlReader;
120                         this.context = context;
121                         this.emptyDictionaryString = emptyDictionaryString;
122                         this.itemName = itemName;
123
124                         this.collectionContract = collectionContract;
125
126                         #endregion
127
128                         ReadGetOnlyCollection (collectionContract);
129                 }
130
131                 void CreateObject (ClassDataContract classContract)
132                 {
133                         Type type = objectType = classContract.UnderlyingType;
134                         if (type.IsValueType && !classContract.IsNonAttributedType)
135                                 type = Globals.TypeOfValueType;
136
137                         if (classContract.UnderlyingType == Globals.TypeOfDBNull)
138                                 objectLocal = DBNull.Value;
139                         else if (classContract.IsNonAttributedType) {
140                                 if (type.IsValueType)
141                                         objectLocal = FormatterServices.GetUninitializedObject (type);
142                                 else
143                                         objectLocal = classContract.GetNonAttributedTypeConstructor ().Invoke (new object [0]);
144                         }
145                         else
146                                 objectLocal = CodeInterpreter.ConvertValue (XmlFormatReaderGenerator.UnsafeGetUninitializedObject (DataContract.GetIdForInitialization (classContract)), Globals.TypeOfObject, type);
147                 }
148
149                 void InvokeOnDeserializing (ClassDataContract classContract)
150                 {
151                         if (classContract.BaseContract != null)
152                                 InvokeOnDeserializing (classContract.BaseContract);
153                         if (classContract.OnDeserializing != null)
154                                 classContract.OnDeserializing.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
155                 }
156
157                 void InvokeOnDeserialized (ClassDataContract classContract)
158                 {
159                         if (classContract.BaseContract != null)
160                                 InvokeOnDeserialized (classContract.BaseContract);
161                         if (classContract.OnDeserialized != null)
162                                 classContract.OnDeserialized.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
163                 }
164
165                 bool HasFactoryMethod (ClassDataContract classContract)
166                 {
167                         return Globals.TypeOfIObjectReference.IsAssignableFrom (classContract.UnderlyingType);
168                 }
169
170                 bool InvokeFactoryMethod (ClassDataContract classContract)
171                 {
172                         if (HasFactoryMethod (classContract)) {
173                                 objectLocal = CodeInterpreter.ConvertValue (context.GetRealObject ((IObjectReference) objectLocal, Globals.NewObjectId), Globals.TypeOfObject, classContract.UnderlyingType);
174                                 return true;
175                         }
176                         return false;
177                 }
178
179                 void ReadISerializable (ClassDataContract classContract)
180                 {
181                         ConstructorInfo ctor = classContract.UnderlyingType.GetConstructor (Globals.ScanAllMembers, null, JsonFormatGeneratorStatics.SerInfoCtorArgs, null);
182                         if (ctor == null)
183                                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError (XmlObjectSerializer.CreateSerializationException (SR.GetString (SR.SerializationInfo_ConstructorNotFound, DataContract.GetClrTypeFullName (classContract.UnderlyingType))));
184                         context.ReadSerializationInfo (xmlReader, classContract.UnderlyingType);
185                         ctor.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
186                 }
187
188                 void ReadClass (ClassDataContract classContract)
189                 {
190                         if (classContract.HasExtensionData) {
191                                 ExtensionDataObject extensionData = new ExtensionDataObject ();
192                                 ReadMembers (classContract, extensionData);
193                                 ClassDataContract currentContract = classContract;
194                                 while (currentContract != null) {
195                                         MethodInfo extensionDataSetMethod = currentContract.ExtensionDataSetMethod;
196                                         if (extensionDataSetMethod != null)
197                                                 extensionDataSetMethod.Invoke (objectLocal, new object [] {extensionData});
198                                         currentContract = currentContract.BaseContract;
199                                 }
200                         }
201                         else
202                                 ReadMembers (classContract, null);
203                 }
204
205                 void ReadMembers (ClassDataContract classContract, ExtensionDataObject  extensionData)
206                 {
207                         int memberCount = classContract.MemberNames.Length;
208                         context.IncrementItemCount (memberCount);
209
210                         int memberIndex = -1;
211                         
212                         // JSON intrinsic part.
213                         BitFlagsGenerator expectedElements = new BitFlagsGenerator (memberCount);
214                         byte [] requiredElements = new byte [expectedElements.GetLocalCount ()];
215                         SetRequiredElements (classContract, requiredElements);
216                         SetExpectedElements (expectedElements, 0 /*startIndex*/);
217
218                         while (XmlObjectSerializerReadContext.MoveToNextElement (xmlReader)) {
219                                 int idx; // used as in "switch (idx)" in the original source.
220                                 idx = context.GetJsonMemberIndex (xmlReader, memberNames, memberIndex, extensionData);
221
222                                 if (memberCount > 0)
223                                         ReadMembers (idx, classContract, expectedElements, ref memberIndex);
224                         }
225
226                         if (!CheckRequiredElements (expectedElements, requiredElements))
227                                 XmlObjectSerializerReadContextComplexJson.ThrowMissingRequiredMembers (objectLocal, memberNames, expectedElements.LoadArray (), requiredElements);
228                 }
229
230                 int ReadMembers (int index, ClassDataContract classContract, BitFlagsGenerator expectedElements, ref int memberIndex)
231                 {
232                         int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers (index, classContract.BaseContract, expectedElements,
233                         ref memberIndex);
234                         
235                         if (memberCount <= index && index < memberCount + classContract.Members.Count) {
236                                 DataMember dataMember = classContract.Members [index - memberCount];
237                                 Type memberType = dataMember.MemberType;
238                                 
239                                 memberIndex = memberCount;
240                                 if (!expectedElements.Load (index))
241                                         XmlObjectSerializerReadContextComplexJson.ThrowDuplicateMemberException (objectLocal, memberNames, memberIndex);
242
243                                 if (dataMember.IsGetOnlyCollection) {
244                                         var value = CodeInterpreter.GetMember (dataMember.MemberInfo, objectLocal);
245                                         context.StoreCollectionMemberInfo (value);
246                                         ReadValue (memberType, dataMember.Name);
247                                 } else {
248                                         var value = ReadValue (memberType, dataMember.Name);
249                                         CodeInterpreter.SetMember (dataMember.MemberInfo, objectLocal, value);
250                                 }
251                                 memberIndex = index;
252                                 ResetExpectedElements (expectedElements, index);
253                         }
254                         return memberCount + classContract.Members.Count;
255                 }
256
257                 bool CheckRequiredElements (BitFlagsGenerator expectedElements, byte [] requiredElements)
258                 {
259                         for (int i = 0; i < requiredElements.Length; i++)
260                                 if ((expectedElements.GetLocal(i) & requiredElements[i]) != 0)
261                                         return false;
262                         return true;
263                 }
264
265                 int SetRequiredElements (ClassDataContract contract, byte [] requiredElements)
266                 {
267                         int memberCount = (contract.BaseContract == null) ? 0 :
268                         SetRequiredElements (contract.BaseContract, requiredElements);
269                         List<DataMember> members = contract.Members;
270                         for (int i = 0; i < members.Count; i++, memberCount++) {
271                                 if (members[i].IsRequired)
272                                         BitFlagsGenerator.SetBit (requiredElements, memberCount);
273                         }
274                         return memberCount;
275                 }
276
277                 void SetExpectedElements (BitFlagsGenerator expectedElements, int startIndex)
278                 {
279                         int memberCount = expectedElements.GetBitCount ();
280                         for (int i = startIndex; i < memberCount; i++)
281                                 expectedElements.Store (i, true);
282                 }
283
284                 void ResetExpectedElements (BitFlagsGenerator expectedElements, int index)
285                 {
286                         expectedElements.Store (index, false);
287                 }
288
289                 object ReadValue (Type type, string name)
290                 {
291                         var valueType = type;
292                         object value = null;
293                         bool shouldAssignNullableValue = false;
294                         int nullables = 0;
295                         while (type.IsGenericType && type.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
296                                 nullables++;
297                                 type = type.GetGenericArguments () [0];
298                         }
299                         
300                         PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (type);
301                         if ((primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) || nullables != 0 || type.IsValueType) {
302                                 context.ReadAttributes (xmlReader);
303                                 string objectId = context.ReadIfNullOrRef (xmlReader, type, DataContract.IsTypeSerializable (type));
304                                 // Deserialize null
305                 if (objectId == Globals.NullObjectId) {
306                                         
307                                         if (nullables != 0)
308                                                 value = Activator.CreateInstance (valueType);
309                                         else if (type.IsValueType)
310                                                 throw new SerializationException (SR.GetString (SR.ValueTypeCannotBeNull, DataContract.GetClrTypeFullName (type)));
311                                         else
312                                                 value = null;
313                                 } else if (objectId == string.Empty) {
314                                         // Deserialize value
315
316                                         // Compare against Globals.NewObjectId, which is set to string.Empty
317                                         
318                                         objectId = context.GetObjectId ();
319                                         
320                                         if (type.IsValueType) {
321                                                 if (!string.IsNullOrEmpty (objectId))
322                                                         throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveId, DataContract.GetClrTypeFullName(type)));
323                                         }
324                                         object innerValueRead = null;
325                                         if (nullables != 0)
326                                                 shouldAssignNullableValue = true;
327
328                                         if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) {
329                                                 value = primitiveContract.XmlFormatReaderMethod.Invoke (xmlReader, new object [0]);
330                                                 if (!type.IsValueType)
331                                                         context.AddNewObject (value);
332                                         }
333                                         else
334                                                         value = InternalDeserialize (type, name);
335                                 } else {
336                                         // Deserialize ref
337                                         if (type.IsValueType)
338                                                 throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveRef, DataContract.GetClrTypeFullName (type)));
339                                         else
340                                                 value = CodeInterpreter.ConvertValue (context.GetExistingObject (objectId, type, name, string.Empty), Globals.TypeOfObject, type);
341                                 }
342
343                                 if (shouldAssignNullableValue) {
344                                         if (objectId != Globals.NullObjectId)
345                                                 value = WrapNullableObject (type, value, valueType, nullables);
346                                 }
347                         }
348                         else
349                                 value = InternalDeserialize (type, name);
350
351                         return value;
352                 }
353
354                 object InternalDeserialize (Type type, string name)
355                 {
356                         Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type;
357                         var obj = context.InternalDeserialize (xmlReader, DataContract.GetId (declaredType.TypeHandle), declaredType.TypeHandle, name, string.Empty);
358
359                         if (type.IsPointer)
360                                 // wow, there is no way to convert void* to object in strongly typed way...
361                                 return JsonFormatGeneratorStatics.UnboxPointer.Invoke (null, new object [] {obj});
362                         else
363                                 return CodeInterpreter.ConvertValue (obj, Globals.TypeOfObject, type);
364                 }
365
366                 object WrapNullableObject (Type innerType, object innerValue, Type outerType, int nullables)
367                 {
368                         var outerValue = innerValue;
369                         for (int i = 1; i < nullables; i++) {
370                                 Type type = Globals.TypeOfNullable.MakeGenericType (innerType);
371                                 outerValue = Activator.CreateInstance (type, new object[] { outerValue });
372                                 innerType = type;
373                         }
374                         return Activator.CreateInstance (outerType, new object[] { outerValue });
375                 }
376
377
378                 void ReadCollection (CollectionDataContract collectionContract)
379                 {
380                         Type type = collectionContract.UnderlyingType;
381                         Type itemType = collectionContract.ItemType;
382                         bool isArray = (collectionContract.Kind == CollectionKind.Array);
383
384                         ConstructorInfo constructor = collectionContract.Constructor;
385
386                         if (type.IsInterface) {
387                                 switch (collectionContract.Kind) {
388                                 case CollectionKind.GenericDictionary:
389                                         type = Globals.TypeOfDictionaryGeneric.MakeGenericType (itemType.GetGenericArguments ());
390                                         constructor = type.GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
391                                         break;
392                                 case CollectionKind.Dictionary:
393                                         type = Globals.TypeOfHashtable;
394                                         constructor = type.GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
395                                         break;
396                                 case CollectionKind.Collection:
397                                 case CollectionKind.GenericCollection:
398                                 case CollectionKind.Enumerable:
399                                 case CollectionKind.GenericEnumerable:
400                                 case CollectionKind.List:
401                                 case CollectionKind.GenericList:
402                                         type = itemType.MakeArrayType ();
403                                         isArray = true;
404                                         break;
405                                 }
406                         }
407
408                         if (!isArray) {
409                                 if (type.IsValueType)
410                                         // FIXME: this is not what the original code does.
411                                         objectLocal = FormatterServices.GetUninitializedObject (type);
412                                 else {
413                                         objectLocal = constructor.Invoke (new object [0]);
414                                         context.AddNewObject (objectLocal);
415                                 }
416                         }
417
418                         bool canReadSimpleDictionary = collectionContract.Kind == CollectionKind.Dictionary ||
419                         collectionContract.Kind == CollectionKind.GenericDictionary;
420
421                         bool readSimpleDictionary = canReadSimpleDictionary & context.UseSimpleDictionaryFormat;
422                         if (readSimpleDictionary)
423                                 ReadSimpleDictionary (collectionContract, itemType);
424                         else {   
425                                 string objectId = context.GetObjectId ();
426
427                                 bool canReadPrimitiveArray = false, readResult = false;
428                                 if (isArray && TryReadPrimitiveArray (itemType, out readResult))
429                                         canReadPrimitiveArray = true;
430
431                                 if (!canReadPrimitiveArray) {
432                                         object growingCollection = null;
433                                         if (isArray)
434                                                 growingCollection = Array.CreateInstance (itemType, 32);
435
436                                         int i = 0;
437                                         // FIXME: I cannot find i++ part, but without that it won't work as expected.
438                                         for (; i < int.MaxValue; i++) {
439                                                 if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
440                                                         context.IncrementItemCount (1);
441                                                         object value = ReadCollectionItem (collectionContract, itemType);
442                                                         if (isArray) {
443                                                                 MethodInfo ensureArraySizeMethod = XmlFormatGeneratorStatics.EnsureArraySizeMethod.MakeGenericMethod (itemType);
444                                                                 growingCollection = ensureArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
445                                                                 ((Array) growingCollection).SetValue (value, i);
446                                                         } else {
447                                                                 StoreCollectionValue (objectLocal, itemType, value, collectionContract);
448                                                         }
449                                                 }
450                                                 else if (IsEndElement ())
451                                                         break;
452                                                 else
453                                                         HandleUnexpectedItemInCollection (ref i);
454                                         }
455
456                                         if (isArray) {
457                                                 MethodInfo trimArraySizeMethod = XmlFormatGeneratorStatics.TrimArraySizeMethod.MakeGenericMethod (itemType);
458                                                 objectLocal = trimArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
459                                                 context.AddNewObjectWithId (objectId, objectLocal);
460                                         }
461                                 }
462                                 else
463                                         context.AddNewObjectWithId (objectId, objectLocal);
464                         }
465                 }
466
467                 void ReadSimpleDictionary (CollectionDataContract collectionContract, Type keyValueType)
468                 {
469                         Type[] keyValueTypes = keyValueType.GetGenericArguments ();
470                         Type keyType = keyValueTypes [0];
471                         Type valueType = keyValueTypes [1];
472
473                         int keyTypeNullableDepth = 0;
474                         Type keyTypeOriginal = keyType;
475                         while (keyType.IsGenericType && keyType.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
476                                 keyTypeNullableDepth++;
477                                 keyType = keyType.GetGenericArguments () [0];
478                         }
479
480                         ClassDataContract keyValueDataContract = (ClassDataContract)collectionContract.ItemContract;
481                         DataContract keyDataContract = keyValueDataContract.Members [0].MemberTypeContract;
482
483                         KeyParseMode keyParseMode = KeyParseMode.Fail;
484
485                         if (keyType == Globals.TypeOfString || keyType == Globals.TypeOfObject) {
486                                 keyParseMode = KeyParseMode.AsString;
487                         } else if (keyType.IsEnum) {
488                                 keyParseMode = KeyParseMode.UsingParseEnum;
489                         } else if (keyDataContract.ParseMethod != null) {
490                                 keyParseMode = KeyParseMode.UsingCustomParse;
491                         }
492
493                         if (keyParseMode == KeyParseMode.Fail) {
494                                 ThrowSerializationException (
495                                 SR.GetString (
496                                 SR.KeyTypeCannotBeParsedInSimpleDictionary,
497                                 DataContract.GetClrTypeFullName (collectionContract.UnderlyingType),
498                                 DataContract.GetClrTypeFullName (keyType)));
499                         } else {
500                                 XmlNodeType nodeType;
501
502                                 while ((nodeType = xmlReader.MoveToContent ()) != XmlNodeType.EndElement) {
503                                         if (nodeType != XmlNodeType.Element)
504                                                 ThrowUnexpectedStateException (XmlNodeType.Element);
505
506                                         context.IncrementItemCount (1);
507
508                                         var jsonMemberName = XmlObjectSerializerReadContextComplexJson.GetJsonMemberName (xmlReader);
509                                         object key = null;
510
511                                         if (keyParseMode == KeyParseMode.UsingParseEnum)
512                                                 key = Enum.Parse (keyType, jsonMemberName);
513                                         else if (keyParseMode == KeyParseMode.UsingCustomParse)
514                                                 key = keyDataContract.ParseMethod.Invoke (null, new object [] {jsonMemberName});
515
516                                         if (keyTypeNullableDepth > 0) {
517                                                 var keyOriginal = WrapNullableObject (keyType, key, valueType, keyTypeNullableDepth);
518                                                 key = keyOriginal;
519                                         }
520
521                                         var value = ReadValue (valueType, String.Empty);
522                                         collectionContract.AddMethod.Invoke (objectLocal, new object[] {key, value});
523                                 }
524                         }
525                 }
526
527                 void ReadGetOnlyCollection (CollectionDataContract collectionContract)
528                 {
529                         Type type = collectionContract.UnderlyingType;
530                         Type itemType = collectionContract.ItemType;
531                         bool isArray = (collectionContract.Kind == CollectionKind.Array);
532                         int size = 0;
533
534                         objectLocal = context.GetCollectionMember ();
535  
536                         bool canReadSimpleDictionary = 
537                                 collectionContract.Kind == CollectionKind.Dictionary ||
538                                 collectionContract.Kind == CollectionKind.GenericDictionary;
539
540                         bool readSimple = canReadSimpleDictionary && context.UseSimpleDictionaryFormat;
541                         if (readSimple) {
542                                 if (objectLocal == null)
543                                         XmlObjectSerializerReadContext.ThrowNullValueReturnedForGetOnlyCollectionException (type);
544                                 else {
545                                         ReadSimpleDictionary(collectionContract, itemType);
546                                         context.CheckEndOfArray (xmlReader, size, this.itemName, emptyDictionaryString);
547                                 }
548                         } else {
549
550                                 //check that items are actually going to be deserialized into the collection
551                                 if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
552                                         if (objectLocal == null)
553                                                 XmlObjectSerializerReadContext.ThrowNullValueReturnedForGetOnlyCollectionException (type);
554                                         else {
555                                                 size = 0;
556                                                 if (isArray)
557                                                         size = ((Array) objectLocal).Length;
558                                                 for (int i = 0; i < int.MaxValue;) {
559                                                         if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
560                                                                 context.IncrementItemCount (1);
561                                                                 var value = ReadCollectionItem (collectionContract, itemType);
562                                                                 if (isArray) {
563                                                                         if (size == i)
564                                                                                 XmlObjectSerializerReadContext.ThrowArrayExceededSizeException (size, type);
565                                                                         else
566                                                                                 ((Array) objectLocal).SetValue (value, i);
567                                                                 } else {
568                                                                         StoreCollectionValue (objectLocal, itemType, value, collectionContract);
569                                                                 }
570                                                         }
571                                                         else if (IsEndElement())
572                                                                 break;
573                                                         else
574                                                                 HandleUnexpectedItemInCollection (ref i);
575                                                 }
576                                                 context.CheckEndOfArray (xmlReader, size, this.itemName, this.emptyDictionaryString);
577                                         }
578                                 }
579                         }
580                 }
581
582                 bool TryReadPrimitiveArray (Type itemType, out bool readResult)
583                 {
584                         readResult = false;
585                         PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (itemType);
586                         if (primitiveContract == null)
587                                 return false;
588
589                         string readArrayMethod = null;
590                         switch (Type.GetTypeCode (itemType))
591                         {
592                         case TypeCode.Boolean:
593                                 readArrayMethod = "TryReadBooleanArray";
594                         break;
595                         case TypeCode.Decimal:
596                                 readArrayMethod = "TryReadDecimalArray";
597                         break;
598                         case TypeCode.Int32:
599                                 readArrayMethod = "TryReadInt32Array";
600                         break;
601                         case TypeCode.Int64:
602                                 readArrayMethod = "TryReadInt64Array";
603                         break;
604                         case TypeCode.Single:
605                                 readArrayMethod = "TryReadSingleArray";
606                         break;
607                         case TypeCode.Double:
608                                 readArrayMethod = "TryReadDoubleArray";
609                                 break;
610                         case TypeCode.DateTime:
611                                 readArrayMethod = "TryReadJsonDateTimeArray";
612                         break;
613                         default:
614                                 break;
615                         }
616                         if (readArrayMethod != null) {
617                                 var mi = typeof (JsonReaderDelegator).GetMethod (readArrayMethod, Globals.ScanAllMembers);
618                                 var args = new object [] {context, itemName, emptyDictionaryString, -1, objectLocal};
619                                 readResult = (bool) mi.Invoke ((JsonReaderDelegator) xmlReader, args);
620                                 objectLocal = args.Last ();
621                                 return true;
622                         }
623                         return false;
624                 }
625
626                 object ReadCollectionItem (CollectionDataContract collectionContract, Type itemType)
627                 {
628                         if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) {
629                                 context.ResetAttributes ();
630                                 var revisedContract = XmlObjectSerializerWriteContextComplexJson.GetRevisedItemContract (collectionContract.ItemContract);
631                                 var v = DataContractJsonSerializer.ReadJsonValue (revisedContract, xmlReader, context);
632                                 return CodeInterpreter.ConvertValue (v, Globals.TypeOfObject, itemType);
633                         }
634                         else
635                                 return ReadValue (itemType, JsonGlobals.itemString);
636                 }
637
638                 void StoreCollectionValue (object collection, Type valueType, object value, CollectionDataContract collectionContract)
639                 {
640                         if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary) {
641                                 ClassDataContract keyValuePairContract = DataContract.GetDataContract (valueType) as ClassDataContract;
642                                 if (keyValuePairContract == null)
643                                         Fx.Assert ("Failed to create contract for KeyValuePair type");
644                                 DataMember keyMember = keyValuePairContract.Members [0];
645                                 DataMember valueMember = keyValuePairContract.Members [1];
646                                 object pkey = CodeInterpreter.GetMember (keyMember.MemberInfo, value);
647                                 object pvalue = CodeInterpreter.GetMember (valueMember.MemberInfo, value);
648                                 
649                                 try {
650                                         collectionContract.AddMethod.Invoke (collection, new object [] {pkey, pvalue});
651                                 } catch (TargetInvocationException ex) {
652                                         if (ex.InnerException != null)
653                                                 throw ex.InnerException;
654                                         else
655                                                 throw;
656                                 }
657                         }
658                         else
659                                 collectionContract.AddMethod.Invoke (collection, new object [] {value});
660                 }
661
662                 void HandleUnexpectedItemInCollection (ref int iterator)
663                 {
664                         if (IsStartElement ()) {
665                                 context.SkipUnknownElement (xmlReader);
666                                 iterator--;
667                         }
668                         else 
669                                 throw XmlObjectSerializerReadContext.CreateUnexpectedStateException (XmlNodeType.Element, xmlReader);
670                 }
671
672                 bool IsStartElement(XmlDictionaryString name, XmlDictionaryString ns)
673                 {
674                         return xmlReader.IsStartElement (name, ns);
675                 }
676
677                 bool IsStartElement()
678                 {
679                         return xmlReader.IsStartElement ();
680                 }
681
682                 bool IsEndElement ()
683                 {
684                         return xmlReader.NodeType == XmlNodeType.EndElement;
685                 }
686
687                 void ThrowUnexpectedStateException (XmlNodeType expectedState)
688                 {
689                         throw XmlObjectSerializerReadContext.CreateUnexpectedStateException (expectedState, xmlReader);
690                 }
691
692                 void ThrowSerializationException (string msg, params object [] values)
693                 {
694                         if (values != null && values.Length > 0)
695                                 msg = string.Format (msg, values);
696                         throw new SerializationException (msg);
697                 }
698
699                 enum KeyParseMode
700                 {
701                         Fail,
702                         AsString,
703                         UsingParseEnum,
704                         UsingCustomParse
705                 }
706         }
707 }