[System.Net] Add support for .pac proxy config scripts on mac
[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 namespace System.Xml.Serialization
44 {
45         [AttributeUsage (AttributeTargets.Class, AllowMultiple = false)]
46         internal class XmlTypeConvertorAttribute : Attribute
47         {
48                 /*
49                  * Bug #12571:
50                  * 
51                  * System.Xml.Linq.XElement should be deserializable from an XmlElement.
52                  * 
53                  * Types can now register a custom deserializer by adding this custom attribute.
54                  * Method is the name of a private 'static method (static object)' method that will
55                  * be invoked to construct an instance of the object.
56                  * 
57                  */
58                 public string Method {
59                         get;
60                         private set;
61                 }
62
63                 public XmlTypeConvertorAttribute (string method)
64                 {
65                         Method = method;
66                 }
67         }
68
69         internal class TypeData
70         {
71                 Type type;
72                 string elementName;
73                 SchemaTypes sType;
74                 Type listItemType;
75                 string typeName;
76                 string fullTypeName;
77                 string csharpName;
78                 string csharpFullName;
79                 TypeData listItemTypeData;
80                 TypeData listTypeData;
81                 TypeData mappedType;
82                 XmlSchemaPatternFacet facet;
83                 MethodInfo typeConvertor;
84                 bool hasPublicConstructor = true;
85                 bool nullableOverride;
86
87                 public TypeData (Type type, string elementName, bool isPrimitive) :
88                         this(type, elementName, isPrimitive, null, null) {}
89
90                 public TypeData (Type type, string elementName, bool isPrimitive, TypeData mappedType, XmlSchemaPatternFacet facet)
91                 {
92 #if NET_2_0
93                         if (type.IsGenericTypeDefinition)
94                                 throw new InvalidOperationException ("Generic type definition cannot be used in serialization. Only specific generic types can be used.");
95 #endif
96                         this.mappedType = mappedType;
97                         this.facet = facet;
98                         this.type = type;
99                         this.typeName = type.Name;
100                         this.fullTypeName = type.FullName.Replace ('+', '.');
101
102                         if (isPrimitive)
103                                 sType = SchemaTypes.Primitive;
104                         else
105                         {
106                                 if (type.IsEnum)
107                                         sType = SchemaTypes.Enum;
108                                 else if (typeof(IXmlSerializable).IsAssignableFrom (type))
109                                         sType = SchemaTypes.XmlSerializable;
110                                 else if (typeof (System.Xml.XmlNode).IsAssignableFrom (type))
111                                         sType = SchemaTypes.XmlNode;
112                                 else if (type.IsArray || typeof(IEnumerable).IsAssignableFrom (type))
113                                         sType = SchemaTypes.Array;
114                                 else
115                                         sType = SchemaTypes.Class;
116                         }
117                         
118                         if (IsListType)
119                                 this.elementName = TypeTranslator.GetArrayName (ListItemTypeData.XmlType);
120                         else
121                                 this.elementName = elementName;
122
123                         if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
124                                 hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
125                         }
126
127                         LookupTypeConvertor ();
128                 }
129
130                 internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
131                 {
132                         this.elementName = xmlType;
133                         this.typeName = typeName;
134                         this.fullTypeName = fullTypeName.Replace ('+', '.');
135                         this.listItemTypeData = listItemTypeData;
136                         this.sType = schemaType;
137                         this.hasPublicConstructor = true;
138                 }
139
140                 void LookupTypeConvertor ()
141                 {
142 #if NET_4_5
143                         // We only need this for System.Xml.Linq.
144                         var convertor = type.GetCustomAttribute<XmlTypeConvertorAttribute> ();
145                         if (convertor != null)
146                                 typeConvertor = type.GetMethod (convertor.Method, BindingFlags.Static | BindingFlags.NonPublic);
147 #endif
148                 }
149
150                 internal void ConvertForAssignment (ref object value)
151                 {
152                         // Has this object registered a custom type converter?
153                         if (typeConvertor != null)
154                                 value = typeConvertor.Invoke (null, new object[] { value });
155                 }
156
157                 public string TypeName
158                 {
159                         get {
160                                 return typeName;
161                         }
162                 }
163                                 
164                 public string XmlType
165                 {
166                         get {
167                                 return elementName;
168                         }
169                 }
170                                 
171                 public Type Type
172                 {
173                         get {
174                                 return type;
175                         }
176                 }
177                                 
178                 public string FullTypeName
179                 {
180                         get {
181                                 return fullTypeName;
182                         }
183                 }
184
185                 public string CSharpName
186                 {
187                         get {
188                                 if (csharpName == null)
189                                         csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
190                                 return csharpName;
191                         }
192                 }
193
194                 public string CSharpFullName
195                 {
196                         get {
197                                 if (csharpFullName == null)
198                                         csharpFullName = (Type == null) ? TypeName : ToCSharpName (Type, true);
199                                 return csharpFullName;
200                         }
201                 }
202
203                 // static Microsoft.CSharp.CSharpCodeProvider csprovider =
204                 //      new Microsoft.CSharp.CSharpCodeProvider ();
205
206                 public static string ToCSharpName (Type type, bool full)
207                 {
208                         // return csprovider.GetTypeOutput (new System.CodeDom.CodeTypeReference (type));
209                         StringBuilder sb = new StringBuilder ();
210                         
211                         if (type.IsArray) {
212                                 sb.Append (ToCSharpName (type.GetElementType (), full));
213                                 sb.Append ('[');
214                                 int rank = type.GetArrayRank ();
215                                 for (int i = 1; i < rank; i++)
216                                         sb.Append (',');
217                                 sb.Append (']');
218                                 return sb.ToString ();
219                         }
220 #if NET_2_0
221                         // Generic nested types return the complete list of type arguments,
222                         // including type arguments for the declaring class. This requires
223                         // some special handling
224                         if (type.IsGenericType && !type.IsGenericTypeDefinition) {
225                                 Type[] args = type.GetGenericArguments ();
226                                 int nt = args.Length - 1;
227                                 Type pt = type;
228                                 // Loop throguh the declaring class chain, consuming type arguments for every
229                                 // generic class in the chain
230                                 while (pt != null) {
231                                         if (sb.Length > 0)
232                                                 sb.Insert (0, '.');
233                                         int i = pt.Name.IndexOf ('`');
234                                         if (i != -1) {
235                                                 int na = nt - int.Parse (pt.Name.Substring (i+1));
236                                                 sb.Insert (0,'>');
237                                                 for (;nt > na; nt--) {
238                                                         sb.Insert (0, ToCSharpName (args[nt],full));
239                                                         if (nt - 1 != na)
240                                                                 sb.Insert (0, ',');
241                                                 }
242                                                 sb.Insert (0,'<');
243                                                 sb.Insert (0, pt.Name.Substring (0, i));
244                                         } else
245                                                 sb.Insert (0, pt.Name);
246                                         pt = pt.DeclaringType;
247                                 }
248                                 if (full && type.Namespace.Length > 0)
249                                         sb.Insert (0, type.Namespace + ".");
250                                 return sb.ToString ();
251                         }
252 #endif
253                         if (type.DeclaringType != null) {
254                                 sb.Append (ToCSharpName (type.DeclaringType, full)).Append ('.');
255                                 sb.Append (type.Name);
256                         }
257                         else {
258                                 if (full && type.Namespace.Length > 0)
259                                         sb.Append (type.Namespace).Append ('.');
260                                 sb.Append (type.Name);
261                         }
262                         return sb.ToString ();
263                 }
264                 
265                 static bool IsKeyword (string name)
266                 {
267                         if (keywordsTable == null) {
268                                 Hashtable t = new Hashtable ();
269                                 foreach (string s in keywords)
270                                         t [s] = s;
271                                 keywordsTable = t;
272                         }
273                         return keywordsTable.Contains (name);
274                 }
275
276                 public SchemaTypes SchemaType
277                 {
278                         get {
279                                 return sType;
280                         }
281                 }
282
283                 public bool IsListType
284                 {
285                         get { return SchemaType == SchemaTypes.Array; }
286                 }
287
288                 public bool IsComplexType
289                 {
290                         get 
291                         { 
292                                 return (SchemaType == SchemaTypes.Class || 
293                                               SchemaType == SchemaTypes.Array ||
294                                               SchemaType == SchemaTypes.Enum ||
295                                               SchemaType == SchemaTypes.XmlNode ||
296                                                   SchemaType == SchemaTypes.XmlSerializable ||
297                                                   !IsXsdType); 
298                         }
299                 }
300
301                 public bool IsValueType
302                 {
303                         get
304                         {
305                                 if (type != null) return type.IsValueType;
306                                 else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
307                         }
308                 }
309
310                 public bool NullableOverride
311                 {
312                         get { return nullableOverride; }
313                 }
314
315                 public bool IsNullable
316                 {
317                         get
318                         {
319                                 if (nullableOverride)
320                                         return true;
321 #if NET_2_0
322                                 return !IsValueType ||
323                                         (type != null &&
324                                          type.IsGenericType &&
325                                          type.GetGenericTypeDefinition () == typeof (Nullable<>));
326 #else
327                                 return !IsValueType;
328 #endif
329                         }
330
331                         set
332                         {
333                                 nullableOverride = value;
334                         }
335                 }
336
337                 public TypeData ListItemTypeData
338                 {
339                         get
340                         {
341                                 if (listItemTypeData == null && type != null)
342                                         listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
343                                 return listItemTypeData;
344                         }
345                 }
346                 
347                 public Type ListItemType
348                 {
349                         get
350                         {
351                                 if (type == null) 
352                                         throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
353
354                                 if (listItemType != null) return listItemType;
355
356                                 Type genericArgument = null;
357
358                                 if (SchemaType != SchemaTypes.Array)
359                                         throw new InvalidOperationException (Type.FullName + " is not a collection");
360                                 else if (type.IsArray) 
361                                         listItemType = type.GetElementType ();
362 #if NET_2_0
363                                 else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
364 #else
365                                 else if (typeof (ICollection).IsAssignableFrom (type))
366 #endif
367                                 {
368                                         if (typeof (IDictionary).IsAssignableFrom (type))
369                                                 throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture,
370                                                         "The type {0} is not supported because it implements" +
371                                                         " IDictionary.", type.FullName));
372
373                                         if (genericArgument != null)
374                                                 listItemType = genericArgument;
375                                         else {
376                                                 PropertyInfo prop = GetIndexerProperty (type);
377                                                 if (prop == null) 
378                                                         throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
379                                                 listItemType = prop.PropertyType;
380                                         }
381
382                                         MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
383                                         if (addMethod == null)
384                                                 throw CreateMissingAddMethodException (type, "ICollection",
385                                                         listItemType);
386                                 }
387                                 else // at this point, we must be dealing with IEnumerable implementation
388                                 {
389                                         MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
390                                         if (met == null) { 
391                                                 // get private implemenation
392                                                 met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
393                                                         BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
394                                                         null, Type.EmptyTypes, null);
395                                         }
396                                         // determine ListItemType using IEnumerator.Current property
397                                         PropertyInfo prop = met.ReturnType.GetProperty ("Current");
398                                         if (prop == null)
399                                                 listItemType = typeof (object);
400                                         else
401                                                 listItemType = prop.PropertyType;
402
403                                         MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
404                                         if (addMethod == null)
405                                                 throw CreateMissingAddMethodException (type, "IEnumerable",
406                                                         listItemType);
407                                 }
408
409                                 return listItemType;
410                         }
411                 }
412
413                 public TypeData ListTypeData
414                 {
415                         get
416                         {
417                                 if (listTypeData != null) return listTypeData;
418                                 
419                                 listTypeData = new TypeData (TypeName + "[]",
420                                         FullTypeName + "[]",
421                                         TypeTranslator.GetArrayName(XmlType),
422                                         SchemaTypes.Array, this);
423
424                                 return listTypeData;
425                         }
426                 }
427
428                 public bool IsXsdType {
429                         get { return mappedType == null; }
430                 }
431
432                 public TypeData MappedType {
433                         get {
434                                 return mappedType != null ? mappedType : this;
435                         }
436                 }
437
438                 public XmlSchemaPatternFacet XmlSchemaPatternFacet {
439                         get {
440                                 return facet;
441                         }
442                 }
443                 
444                 public bool HasPublicConstructor
445                 {
446                         get { return hasPublicConstructor; }
447                 }
448
449
450                 public static PropertyInfo GetIndexerProperty (Type collectionType)
451                 {
452                         PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
453                         foreach (PropertyInfo prop in props)
454                         {
455                                 ParameterInfo[] pi = prop.GetIndexParameters ();
456                                 if (pi != null && pi.Length == 1 && pi[0].ParameterType == typeof(int))
457                                         return prop;
458                         }
459                         return null;
460                 }
461
462                 private static InvalidOperationException CreateMissingAddMethodException (Type type, string inheritFrom, Type argumentType) {
463                         return new InvalidOperationException (string.Format(CultureInfo.InvariantCulture,
464                                 "To be XML serializable, types which inherit from {0} must have " +
465                                 "an implementation of Add({1}) at all levels of their inheritance " +
466                                 "hierarchy. {2} does not implement Add({1}).", inheritFrom, 
467                                 argumentType.FullName, type.FullName));
468                 }
469
470                 private static Hashtable keywordsTable;
471                 private static string[] keywords = new string[] {
472                         "abstract","event","new","struct","as","explicit","null","switch","base","extern",
473                         "this","false","operator","throw","break","finally","out","true",
474                         "fixed","override","try","case","params","typeof","catch","for",
475                         "private","foreach","protected","checked","goto","public",
476                         "unchecked","class","if","readonly","unsafe","const","implicit","ref",
477                         "continue","in","return","using","virtual","default",
478                         "interface","sealed","volatile","delegate","internal","do","is",
479                         "sizeof","while","lock","stackalloc","else","static","enum",
480                         "namespace",
481                         "object","bool","byte","float","uint","char","ulong","ushort",
482                         "decimal","int","sbyte","short","double","long","string","void",
483 #if NET_2_0
484                         "partial", "yield", "where"
485 #endif
486                 };
487
488 #if NET_2_0
489                 internal static Type GetGenericListItemType (Type type)
490                 {
491                         if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type.GetGenericTypeDefinition ())) {
492                                 Type [] gatypes = type.GetGenericArguments ();
493                                 if (type.GetMethod ("Add", gatypes) != null)
494                                         return gatypes [0];
495                         }
496                         Type t = null;
497                         foreach (Type i in type.GetInterfaces ())
498                                 if ((t = GetGenericListItemType (i)) != null)
499                                         return t;
500                         return null;
501                 }
502 #endif
503         }
504 }