Merge pull request #129 from grumpydev/CryptoFixo
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / TypeData.cs
1 //
2 // System.Xml.Serialization.TypeData
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //  Lluis Sanchez Gual (lluis@ximian.com)
7 //  Atsushi Enomoto (atsushi@ximian.com)
8 //
9 // (C) 2002 Ximian, Inc (http://www.ximian.com)
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 #if NET_2_0
36 using System.Collections.Generic;
37 #endif
38 using System.Globalization;
39 using System.Reflection;
40 using System.Text;
41 using System.Xml.Schema;
42
43 #if MOONLIGHT
44 using XmlSchemaPatternFacet = System.Object;
45 #endif
46
47 namespace System.Xml.Serialization
48 {
49         internal class TypeData
50         {
51                 Type type;
52                 string elementName;
53                 SchemaTypes sType;
54                 Type listItemType;
55                 string typeName;
56                 string fullTypeName;
57                 string csharpName;
58                 string csharpFullName;
59                 TypeData listItemTypeData;
60                 TypeData listTypeData;
61                 TypeData mappedType;
62                 XmlSchemaPatternFacet facet;
63                 bool hasPublicConstructor = true;
64                 bool nullableOverride;
65
66                 public TypeData (Type type, string elementName, bool isPrimitive) :
67                         this(type, elementName, isPrimitive, null, null) {}
68
69                 public TypeData (Type type, string elementName, bool isPrimitive, TypeData mappedType, XmlSchemaPatternFacet facet)
70                 {
71 #if NET_2_0
72                         if (type.IsGenericTypeDefinition)
73                                 throw new InvalidOperationException ("Generic type definition cannot be used in serialization. Only specific generic types can be used.");
74 #endif
75                         this.mappedType = mappedType;
76                         this.facet = facet;
77                         this.type = type;
78                         this.typeName = type.Name;
79                         this.fullTypeName = type.FullName.Replace ('+', '.');
80
81                         if (isPrimitive)
82                                 sType = SchemaTypes.Primitive;
83                         else
84                         {
85                                 if (type.IsEnum)
86                                         sType = SchemaTypes.Enum;
87                                 else if (typeof(IXmlSerializable).IsAssignableFrom (type))
88                                         sType = SchemaTypes.XmlSerializable;
89 #if !MOONLIGHT
90                                 else if (typeof (System.Xml.XmlNode).IsAssignableFrom (type))
91                                         sType = SchemaTypes.XmlNode;
92 #endif
93                                 else if (type.IsArray || typeof(IEnumerable).IsAssignableFrom (type))
94                                         sType = SchemaTypes.Array;
95                                 else
96                                         sType = SchemaTypes.Class;
97                         }
98                         
99                         if (IsListType)
100                                 this.elementName = TypeTranslator.GetArrayName (ListItemTypeData.XmlType);
101                         else
102                                 this.elementName = elementName;
103
104                         if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
105                                 hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
106                         }
107                 }
108
109                 internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
110                 {
111                         this.elementName = xmlType;
112                         this.typeName = typeName;
113                         this.fullTypeName = fullTypeName.Replace ('+', '.');
114                         this.listItemTypeData = listItemTypeData;
115                         this.sType = schemaType;
116                         this.hasPublicConstructor = true;
117                 }
118
119                 public string TypeName
120                 {
121                         get {
122                                 return typeName;
123                         }
124                 }
125                                 
126                 public string XmlType
127                 {
128                         get {
129                                 return elementName;
130                         }
131                 }
132                                 
133                 public Type Type
134                 {
135                         get {
136                                 return type;
137                         }
138                 }
139                                 
140                 public string FullTypeName
141                 {
142                         get {
143                                 return fullTypeName;
144                         }
145                 }
146
147                 public string CSharpName
148                 {
149                         get {
150                                 if (csharpName == null)
151                                         csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
152                                 return csharpName;
153                         }
154                 }
155
156                 public string CSharpFullName
157                 {
158                         get {
159                                 if (csharpFullName == null)
160                                         csharpFullName = (Type == null) ? TypeName : ToCSharpName (Type, true);
161                                 return csharpFullName;
162                         }
163                 }
164
165                 // static Microsoft.CSharp.CSharpCodeProvider csprovider =
166                 //      new Microsoft.CSharp.CSharpCodeProvider ();
167
168                 public static string ToCSharpName (Type type, bool full)
169                 {
170                         // return csprovider.GetTypeOutput (new System.CodeDom.CodeTypeReference (type));
171                         StringBuilder sb = new StringBuilder ();
172                         
173                         if (type.IsArray) {
174                                 sb.Append (ToCSharpName (type.GetElementType (), full));
175                                 sb.Append ('[');
176                                 int rank = type.GetArrayRank ();
177                                 for (int i = 1; i < rank; i++)
178                                         sb.Append (',');
179                                 sb.Append (']');
180                                 return sb.ToString ();
181                         }
182 #if NET_2_0
183                         // Generic nested types return the complete list of type arguments,
184                         // including type arguments for the declaring class. This requires
185                         // some special handling
186                         if (type.IsGenericType && !type.IsGenericTypeDefinition) {
187                                 Type[] args = type.GetGenericArguments ();
188                                 int nt = args.Length - 1;
189                                 Type pt = type;
190                                 // Loop throguh the declaring class chain, consuming type arguments for every
191                                 // generic class in the chain
192                                 while (pt != null) {
193                                         if (sb.Length > 0)
194                                                 sb.Insert (0, '.');
195                                         int i = pt.Name.IndexOf ('`');
196                                         if (i != -1) {
197                                                 int na = nt - int.Parse (pt.Name.Substring (i+1));
198                                                 sb.Insert (0,'>');
199                                                 for (;nt > na; nt--) {
200                                                         sb.Insert (0, ToCSharpName (args[nt],full));
201                                                         if (nt - 1 != na)
202                                                                 sb.Insert (0, ',');
203                                                 }
204                                                 sb.Insert (0,'<');
205                                                 sb.Insert (0, pt.Name.Substring (0, i));
206                                         } else
207                                                 sb.Insert (0, pt.Name);
208                                         pt = pt.DeclaringType;
209                                 }
210                                 if (full && type.Namespace.Length > 0)
211                                         sb.Insert (0, type.Namespace + ".");
212                                 return sb.ToString ();
213                         }
214 #endif
215                         if (type.DeclaringType != null) {
216                                 sb.Append (ToCSharpName (type.DeclaringType, full)).Append ('.');
217                                 sb.Append (type.Name);
218                         }
219                         else {
220                                 if (full && type.Namespace.Length > 0)
221                                         sb.Append (type.Namespace).Append ('.');
222                                 sb.Append (type.Name);
223                         }
224                         return sb.ToString ();
225                 }
226                 
227                 static bool IsKeyword (string name)
228                 {
229                         if (keywordsTable == null) {
230                                 Hashtable t = new Hashtable ();
231                                 foreach (string s in keywords)
232                                         t [s] = s;
233                                 keywordsTable = t;
234                         }
235                         return keywordsTable.Contains (name);
236                 }
237
238                 public SchemaTypes SchemaType
239                 {
240                         get {
241                                 return sType;
242                         }
243                 }
244
245                 public bool IsListType
246                 {
247                         get { return SchemaType == SchemaTypes.Array; }
248                 }
249
250                 public bool IsComplexType
251                 {
252                         get 
253                         { 
254                                 return (SchemaType == SchemaTypes.Class || 
255                                               SchemaType == SchemaTypes.Array ||
256                                               SchemaType == SchemaTypes.Enum ||
257                                               SchemaType == SchemaTypes.XmlNode ||
258                                                   SchemaType == SchemaTypes.XmlSerializable ||
259                                                   !IsXsdType); 
260                         }
261                 }
262
263                 public bool IsValueType
264                 {
265                         get
266                         {
267                                 if (type != null) return type.IsValueType;
268                                 else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
269                         }
270                 }
271
272                 public bool NullableOverride
273                 {
274                         get { return nullableOverride; }
275                 }
276
277                 public bool IsNullable
278                 {
279                         get
280                         {
281                                 if (nullableOverride)
282                                         return true;
283 #if NET_2_0
284                                 return !IsValueType ||
285                                         (type != null &&
286                                          type.IsGenericType &&
287                                          type.GetGenericTypeDefinition () == typeof (Nullable<>));
288 #else
289                                 return !IsValueType;
290 #endif
291                         }
292
293                         set
294                         {
295                                 nullableOverride = value;
296                         }
297                 }
298
299                 public TypeData ListItemTypeData
300                 {
301                         get
302                         {
303                                 if (listItemTypeData == null && type != null)
304                                         listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
305                                 return listItemTypeData;
306                         }
307                 }
308                 
309                 public Type ListItemType
310                 {
311                         get
312                         {
313                                 if (type == null) 
314                                         throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
315
316                                 if (listItemType != null) return listItemType;
317
318                                 Type genericArgument = null;
319
320                                 if (SchemaType != SchemaTypes.Array)
321                                         throw new InvalidOperationException (Type.FullName + " is not a collection");
322                                 else if (type.IsArray) 
323                                         listItemType = type.GetElementType ();
324 #if NET_2_0
325                                 else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
326 #else
327                                 else if (typeof (ICollection).IsAssignableFrom (type))
328 #endif
329                                 {
330                                         if (typeof (IDictionary).IsAssignableFrom (type))
331                                                 throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture,
332                                                         "The type {0} is not supported because it implements" +
333                                                         " IDictionary.", type.FullName));
334
335                                         if (genericArgument != null)
336                                                 listItemType = genericArgument;
337                                         else {
338                                                 PropertyInfo prop = GetIndexerProperty (type);
339                                                 if (prop == null) 
340                                                         throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
341                                                 listItemType = prop.PropertyType;
342                                         }
343
344                                         MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
345                                         if (addMethod == null)
346                                                 throw CreateMissingAddMethodException (type, "ICollection",
347                                                         listItemType);
348                                 }
349                                 else // at this point, we must be dealing with IEnumerable implementation
350                                 {
351                                         MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
352                                         if (met == null) { 
353                                                 // get private implemenation
354                                                 met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
355                                                         BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
356                                                         null, Type.EmptyTypes, null);
357                                         }
358                                         // determine ListItemType using IEnumerator.Current property
359                                         PropertyInfo prop = met.ReturnType.GetProperty ("Current");
360                                         if (prop == null)
361                                                 listItemType = typeof (object);
362                                         else
363                                                 listItemType = prop.PropertyType;
364
365                                         MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
366                                         if (addMethod == null)
367                                                 throw CreateMissingAddMethodException (type, "IEnumerable",
368                                                         listItemType);
369                                 }
370
371                                 return listItemType;
372                         }
373                 }
374
375                 public TypeData ListTypeData
376                 {
377                         get
378                         {
379                                 if (listTypeData != null) return listTypeData;
380                                 
381                                 listTypeData = new TypeData (TypeName + "[]",
382                                         FullTypeName + "[]",
383                                         TypeTranslator.GetArrayName(XmlType),
384                                         SchemaTypes.Array, this);
385
386                                 return listTypeData;
387                         }
388                 }
389
390                 public bool IsXsdType {
391                         get { return mappedType == null; }
392                 }
393
394                 public TypeData MappedType {
395                         get {
396                                 return mappedType != null ? mappedType : this;
397                         }
398                 }
399
400                 public XmlSchemaPatternFacet XmlSchemaPatternFacet {
401                         get {
402                                 return facet;
403                         }
404                 }
405                 
406                 public bool HasPublicConstructor
407                 {
408                         get { return hasPublicConstructor; }
409                 }
410
411
412                 public static PropertyInfo GetIndexerProperty (Type collectionType)
413                 {
414                         PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
415                         foreach (PropertyInfo prop in props)
416                         {
417                                 ParameterInfo[] pi = prop.GetIndexParameters ();
418                                 if (pi != null && pi.Length == 1 && pi[0].ParameterType == typeof(int))
419                                         return prop;
420                         }
421                         return null;
422                 }
423
424                 private static InvalidOperationException CreateMissingAddMethodException (Type type, string inheritFrom, Type argumentType) {
425                         return new InvalidOperationException (string.Format(CultureInfo.InvariantCulture,
426                                 "To be XML serializable, types which inherit from {0} must have " +
427                                 "an implementation of Add({1}) at all levels of their inheritance " +
428                                 "hierarchy. {2} does not implement Add({1}).", inheritFrom, 
429                                 argumentType.FullName, type.FullName));
430                 }
431
432                 private static Hashtable keywordsTable;
433                 private static string[] keywords = new string[] {
434                         "abstract","event","new","struct","as","explicit","null","switch","base","extern",
435                         "this","false","operator","throw","break","finally","out","true",
436                         "fixed","override","try","case","params","typeof","catch","for",
437                         "private","foreach","protected","checked","goto","public",
438                         "unchecked","class","if","readonly","unsafe","const","implicit","ref",
439                         "continue","in","return","using","virtual","default",
440                         "interface","sealed","volatile","delegate","internal","do","is",
441                         "sizeof","while","lock","stackalloc","else","static","enum",
442                         "namespace",
443                         "object","bool","byte","float","uint","char","ulong","ushort",
444                         "decimal","int","sbyte","short","double","long","string","void",
445 #if NET_2_0
446                         "partial", "yield", "where"
447 #endif
448                 };
449
450 #if NET_2_0
451                 internal static Type GetGenericListItemType (Type type)
452                 {
453                         if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type.GetGenericTypeDefinition ())) {
454                                 Type [] gatypes = type.GetGenericArguments ();
455                                 if (type.GetMethod ("Add", gatypes) != null)
456                                         return gatypes [0];
457                         }
458                         Type t = null;
459                         foreach (Type i in type.GetInterfaces ())
460                                 if ((t = GetGenericListItemType (i)) != null)
461                                         return t;
462                         return null;
463                 }
464 #endif
465         }
466 }