2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.XML / System.Xml.Serialization / TypeData.cs
blob00e9beb396e173de4f1360d3494bfc24d24d6189
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)
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.
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;
43 namespace System.Xml.Serialization
45 internal class TypeData
47 Type type;
48 string elementName;
49 SchemaTypes sType;
50 Type listItemType;
51 string typeName;
52 string fullTypeName;
53 string csharpName;
54 string csharpFullName;
55 TypeData listItemTypeData;
56 TypeData listTypeData;
57 TypeData mappedType;
58 XmlSchemaPatternFacet facet;
59 bool hasPublicConstructor = true;
60 bool nullableOverride;
62 public TypeData (Type type, string elementName, bool isPrimitive) :
63 this(type, elementName, isPrimitive, null, null) {}
65 public TypeData (Type type, string elementName, bool isPrimitive, TypeData mappedType, XmlSchemaPatternFacet facet)
67 #if NET_2_0
68 if (type.IsGenericTypeDefinition)
69 throw new InvalidOperationException ("Generic type definition cannot be used in serialization. Only specific generic types can be used.");
70 #endif
71 this.mappedType = mappedType;
72 this.facet = facet;
73 this.type = type;
74 this.typeName = type.Name;
75 this.fullTypeName = type.FullName.Replace ('+', '.');
77 if (isPrimitive)
78 sType = SchemaTypes.Primitive;
79 else
81 if (type.IsEnum)
82 sType = SchemaTypes.Enum;
83 else if (typeof(IXmlSerializable).IsAssignableFrom (type))
84 sType = SchemaTypes.XmlSerializable;
85 else if (typeof (System.Xml.XmlNode).IsAssignableFrom (type))
86 sType = SchemaTypes.XmlNode;
87 else if (type.IsArray || typeof(IEnumerable).IsAssignableFrom (type))
88 sType = SchemaTypes.Array;
89 else
90 sType = SchemaTypes.Class;
93 if (IsListType)
94 this.elementName = TypeTranslator.GetArrayName (ListItemTypeData.XmlType);
95 else
96 this.elementName = elementName;
98 if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
99 hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
103 internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
105 this.elementName = xmlType;
106 this.typeName = typeName;
107 this.fullTypeName = fullTypeName.Replace ('+', '.');
108 this.listItemTypeData = listItemTypeData;
109 this.sType = schemaType;
110 this.hasPublicConstructor = true;
113 public string TypeName
115 get {
116 return typeName;
120 public string XmlType
122 get {
123 return elementName;
127 public Type Type
129 get {
130 return type;
134 public string FullTypeName
136 get {
137 return fullTypeName;
141 public string CSharpName
143 get {
144 if (csharpName == null)
145 csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
146 return csharpName;
150 public string CSharpFullName
152 get {
153 if (csharpFullName == null)
154 csharpFullName = (Type == null) ? TypeName : ToCSharpName (Type, true);
155 return csharpFullName;
159 // static Microsoft.CSharp.CSharpCodeProvider csprovider =
160 // new Microsoft.CSharp.CSharpCodeProvider ();
162 public static string ToCSharpName (Type type, bool full)
164 // return csprovider.GetTypeOutput (new System.CodeDom.CodeTypeReference (type));
165 if (type.IsArray) {
166 StringBuilder sb = new StringBuilder ();
167 sb.Append (ToCSharpName (type.GetElementType (), full));
168 sb.Append ('[');
169 int rank = type.GetArrayRank ();
170 for (int i = 1; i < rank; i++)
171 sb.Append (',');
172 sb.Append (']');
173 return sb.ToString ();
175 #if NET_2_0
176 if (type.IsGenericType && !type.IsGenericTypeDefinition) {
177 StringBuilder sb = new StringBuilder ();
178 sb.Append (ToCSharpName (type.GetGenericTypeDefinition (), full));
179 sb.Append ('<');
180 foreach (Type arg in type.GetGenericArguments ())
181 sb.Append (ToCSharpName (arg, full)).Append (',');
182 sb.Length--;
183 sb.Append ('>');
184 return sb.ToString ();
186 #endif
187 string name = full ? type.FullName : type.Name;
188 name = name.Replace ('+', '.');
189 int idx = name.IndexOf ('`'); // generic definition has extra `n.
190 name = idx > 0 ? name.Substring (0, idx) : name;
191 if (IsKeyword (name))
192 return "@" + name;
193 else
194 return name;
197 static bool IsKeyword (string name)
199 if (keywordsTable == null) {
200 Hashtable t = new Hashtable ();
201 foreach (string s in keywords)
202 t [s] = s;
203 keywordsTable = t;
205 return keywordsTable.Contains (name);
208 public SchemaTypes SchemaType
210 get {
211 return sType;
215 public bool IsListType
217 get { return SchemaType == SchemaTypes.Array; }
220 public bool IsComplexType
222 get
224 return (SchemaType == SchemaTypes.Class ||
225 SchemaType == SchemaTypes.Array ||
226 SchemaType == SchemaTypes.Enum ||
227 SchemaType == SchemaTypes.XmlNode ||
228 SchemaType == SchemaTypes.XmlSerializable ||
229 !IsXsdType);
233 public bool IsValueType
237 if (type != null) return type.IsValueType;
238 else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
242 public bool NullableOverride
244 get { return nullableOverride; }
247 public bool IsNullable
251 if (nullableOverride)
252 return true;
253 #if NET_2_0
254 return !IsValueType ||
255 (type != null &&
256 type.IsGenericType &&
257 type.GetGenericTypeDefinition () == typeof (Nullable<>));
258 #else
259 return !IsValueType;
260 #endif
265 nullableOverride = value;
269 public TypeData ListItemTypeData
273 if (listItemTypeData == null && type != null)
274 listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
275 return listItemTypeData;
279 public Type ListItemType
283 if (type == null)
284 throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
286 if (listItemType != null) return listItemType;
288 Type genericArgument = null;
290 if (SchemaType != SchemaTypes.Array)
291 throw new InvalidOperationException (Type.FullName + " is not a collection");
292 else if (type.IsArray)
293 listItemType = type.GetElementType ();
294 #if NET_2_0
295 else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
296 #else
297 else if (typeof (ICollection).IsAssignableFrom (type))
298 #endif
300 if (typeof (IDictionary).IsAssignableFrom (type))
301 throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture,
302 "The type {0} is not supported because it implements" +
303 " IDictionary.", type.FullName));
305 if (genericArgument != null)
306 listItemType = genericArgument;
307 else {
308 PropertyInfo prop = GetIndexerProperty (type);
309 if (prop == null)
310 throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
311 listItemType = prop.PropertyType;
314 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
315 if (addMethod == null)
316 throw CreateMissingAddMethodException (type, "ICollection",
317 listItemType);
319 else // at this point, we must be dealing with IEnumerable implementation
321 MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
322 if (met == null) {
323 // get private implemenation
324 met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
325 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
326 null, Type.EmptyTypes, null);
328 // determine ListItemType using IEnumerator.Current property
329 PropertyInfo prop = met.ReturnType.GetProperty ("Current");
330 if (prop == null)
331 listItemType = typeof (object);
332 else
333 listItemType = prop.PropertyType;
335 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
336 if (addMethod == null)
337 throw CreateMissingAddMethodException (type, "IEnumerable",
338 listItemType);
341 return listItemType;
345 public TypeData ListTypeData
349 if (listTypeData != null) return listTypeData;
351 listTypeData = new TypeData (TypeName + "[]",
352 FullTypeName + "[]",
353 TypeTranslator.GetArrayName(XmlType),
354 SchemaTypes.Array, this);
356 return listTypeData;
360 public bool IsXsdType {
361 get { return mappedType == null; }
364 public TypeData MappedType {
365 get {
366 return mappedType != null ? mappedType : this;
370 public XmlSchemaPatternFacet XmlSchemaPatternFacet {
371 get {
372 return facet;
376 public bool HasPublicConstructor
378 get { return hasPublicConstructor; }
382 public static PropertyInfo GetIndexerProperty (Type collectionType)
384 PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
385 foreach (PropertyInfo prop in props)
387 ParameterInfo[] pi = prop.GetIndexParameters ();
388 if (pi != null && pi.Length == 1 && pi[0].ParameterType == typeof(int))
389 return prop;
391 return null;
394 private static InvalidOperationException CreateMissingAddMethodException (Type type, string inheritFrom, Type argumentType) {
395 return new InvalidOperationException (string.Format(CultureInfo.InvariantCulture,
396 "To be XML serializable, types which inherit from {0} must have " +
397 "an implementation of Add({1}) at all levels of their inheritance " +
398 "hierarchy. {2} does not implement Add({1}).", inheritFrom,
399 argumentType.FullName, type.FullName));
402 private static Hashtable keywordsTable;
403 private static string[] keywords = new string[] {
404 "abstract","event","new","struct","as","explicit","null","switch","base","extern",
405 "this","false","operator","throw","break","finally","out","true",
406 "fixed","override","try","case","params","typeof","catch","for",
407 "private","foreach","protected","checked","goto","public",
408 "unchecked","class","if","readonly","unsafe","const","implicit","ref",
409 "continue","in","return","using","virtual","default",
410 "interface","sealed","volatile","delegate","internal","do","is",
411 "sizeof","while","lock","stackalloc","else","static","enum",
412 "namespace",
413 "object","bool","byte","float","uint","char","ulong","ushort",
414 "decimal","int","sbyte","short","double","long","string","void",
415 #if NET_2_0
416 "partial", "yield", "where"
417 #endif
420 #if NET_2_0
421 private Type GetGenericListItemType (Type type)
423 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (ICollection<>))
424 return type.GetGenericArguments () [0];
425 Type t = null;
426 foreach (Type i in type.GetInterfaces ())
427 if ((t = GetGenericListItemType (i)) != null)
428 return t;
429 return null;
431 #endif