2 // System.Xml.Serialization.TypeData
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 // Lluis Sanchez Gual (lluis@ximian.com)
7 // Atsushi Enomoto (atsushi@ximian.com)
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
34 using System.Collections;
36 using System.Collections.Generic;
38 using System.Globalization;
39 using System.Reflection;
41 using System.Xml.Schema;
43 namespace System.Xml.Serialization
45 [AttributeUsage (AttributeTargets.Class, AllowMultiple = false)]
46 internal class XmlTypeConvertorAttribute : Attribute
51 * System.Xml.Linq.XElement should be deserializable from an XmlElement.
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.
58 public string Method {
63 public XmlTypeConvertorAttribute (string method)
69 internal class TypeData
78 string csharpFullName;
79 TypeData listItemTypeData;
80 TypeData listTypeData;
82 XmlSchemaPatternFacet facet;
83 MethodInfo typeConvertor;
84 bool hasPublicConstructor = true;
85 bool nullableOverride;
87 public TypeData (Type type, string elementName, bool isPrimitive) :
88 this(type, elementName, isPrimitive, null, null) {}
90 public TypeData (Type type, string elementName, bool isPrimitive, TypeData mappedType, XmlSchemaPatternFacet facet)
93 if (type.IsGenericTypeDefinition)
94 throw new InvalidOperationException ("Generic type definition cannot be used in serialization. Only specific generic types can be used.");
96 this.mappedType = mappedType;
99 this.typeName = type.Name;
100 this.fullTypeName = type.FullName.Replace ('+', '.');
103 sType = SchemaTypes.Primitive;
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;
115 sType = SchemaTypes.Class;
119 this.elementName = TypeTranslator.GetArrayName (ListItemTypeData.XmlType);
121 this.elementName = elementName;
123 if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
124 hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
127 LookupTypeConvertor ();
130 internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
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;
139 LookupTypeConvertor ();
142 void LookupTypeConvertor ()
145 // We only need this for System.Xml.Linq.
146 var convertor = type.GetCustomAttribute<XmlTypeConvertorAttribute> ();
147 if (convertor != null)
148 typeConvertor = type.GetMethod (convertor.Method, BindingFlags.Static | BindingFlags.NonPublic);
152 internal void ConvertForAssignment (ref object value)
154 // Has this object registered a custom type converter?
155 if (typeConvertor != null)
156 value = typeConvertor.Invoke (null, new object[] { value });
159 public string TypeName
166 public string XmlType
180 public string FullTypeName
187 public string CSharpName
190 if (csharpName == null)
191 csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
196 public string CSharpFullName
199 if (csharpFullName == null)
200 csharpFullName = (Type == null) ? TypeName : ToCSharpName (Type, true);
201 return csharpFullName;
205 // static Microsoft.CSharp.CSharpCodeProvider csprovider =
206 // new Microsoft.CSharp.CSharpCodeProvider ();
208 public static string ToCSharpName (Type type, bool full)
210 // return csprovider.GetTypeOutput (new System.CodeDom.CodeTypeReference (type));
211 StringBuilder sb = new StringBuilder ();
214 sb.Append (ToCSharpName (type.GetElementType (), full));
216 int rank = type.GetArrayRank ();
217 for (int i = 1; i < rank; i++)
220 return sb.ToString ();
223 // Generic nested types return the complete list of type arguments,
224 // including type arguments for the declaring class. This requires
225 // some special handling
226 if (type.IsGenericType && !type.IsGenericTypeDefinition) {
227 Type[] args = type.GetGenericArguments ();
228 int nt = args.Length - 1;
230 // Loop throguh the declaring class chain, consuming type arguments for every
231 // generic class in the chain
235 int i = pt.Name.IndexOf ('`');
237 int na = nt - int.Parse (pt.Name.Substring (i+1));
239 for (;nt > na; nt--) {
240 sb.Insert (0, ToCSharpName (args[nt],full));
245 sb.Insert (0, pt.Name.Substring (0, i));
247 sb.Insert (0, pt.Name);
248 pt = pt.DeclaringType;
250 if (full && type.Namespace.Length > 0)
251 sb.Insert (0, type.Namespace + ".");
252 return sb.ToString ();
255 if (type.DeclaringType != null) {
256 sb.Append (ToCSharpName (type.DeclaringType, full)).Append ('.');
257 sb.Append (type.Name);
260 if (full && type.Namespace.Length > 0)
261 sb.Append (type.Namespace).Append ('.');
262 sb.Append (type.Name);
264 return sb.ToString ();
267 static bool IsKeyword (string name)
269 if (keywordsTable == null) {
270 Hashtable t = new Hashtable ();
271 foreach (string s in keywords)
275 return keywordsTable.Contains (name);
278 public SchemaTypes SchemaType
285 public bool IsListType
287 get { return SchemaType == SchemaTypes.Array; }
290 public bool IsComplexType
294 return (SchemaType == SchemaTypes.Class ||
295 SchemaType == SchemaTypes.Array ||
296 SchemaType == SchemaTypes.Enum ||
297 SchemaType == SchemaTypes.XmlNode ||
298 SchemaType == SchemaTypes.XmlSerializable ||
303 public bool IsValueType
307 if (type != null) return type.IsValueType;
308 else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
312 public bool NullableOverride
314 get { return nullableOverride; }
317 public bool IsNullable
321 if (nullableOverride)
324 return !IsValueType ||
326 type.IsGenericType &&
327 type.GetGenericTypeDefinition () == typeof (Nullable<>));
335 nullableOverride = value;
339 public TypeData ListItemTypeData
343 if (listItemTypeData == null && type != null)
344 listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
345 return listItemTypeData;
349 public Type ListItemType
354 throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
356 if (listItemType != null) return listItemType;
358 Type genericArgument = null;
360 if (SchemaType != SchemaTypes.Array)
361 throw new InvalidOperationException (Type.FullName + " is not a collection");
362 else if (type.IsArray)
363 listItemType = type.GetElementType ();
365 else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
367 else if (typeof (ICollection).IsAssignableFrom (type))
370 if (typeof (IDictionary).IsAssignableFrom (type))
371 throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture,
372 "The type {0} is not supported because it implements" +
373 " IDictionary.", type.FullName));
375 if (genericArgument != null)
376 listItemType = genericArgument;
378 PropertyInfo prop = GetIndexerProperty (type);
380 throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
381 listItemType = prop.PropertyType;
384 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
385 if (addMethod == null)
386 throw CreateMissingAddMethodException (type, "ICollection",
389 else // at this point, we must be dealing with IEnumerable implementation
391 MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
393 // get private implemenation
394 met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
395 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
396 null, Type.EmptyTypes, null);
398 // determine ListItemType using IEnumerator.Current property
399 PropertyInfo prop = met.ReturnType.GetProperty ("Current");
401 listItemType = typeof (object);
403 listItemType = prop.PropertyType;
405 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
406 if (addMethod == null)
407 throw CreateMissingAddMethodException (type, "IEnumerable",
415 public TypeData ListTypeData
419 if (listTypeData != null) return listTypeData;
421 listTypeData = new TypeData (TypeName + "[]",
423 TypeTranslator.GetArrayName(XmlType),
424 SchemaTypes.Array, this);
430 public bool IsXsdType {
431 get { return mappedType == null; }
434 public TypeData MappedType {
436 return mappedType != null ? mappedType : this;
440 public XmlSchemaPatternFacet XmlSchemaPatternFacet {
446 public bool HasPublicConstructor
448 get { return hasPublicConstructor; }
452 public static PropertyInfo GetIndexerProperty (Type collectionType)
454 PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
455 foreach (PropertyInfo prop in props)
457 ParameterInfo[] pi = prop.GetIndexParameters ();
458 if (pi != null && pi.Length == 1 && pi[0].ParameterType == typeof(int))
464 private static InvalidOperationException CreateMissingAddMethodException (Type type, string inheritFrom, Type argumentType) {
465 return new InvalidOperationException (string.Format(CultureInfo.InvariantCulture,
466 "To be XML serializable, types which inherit from {0} must have " +
467 "an implementation of Add({1}) at all levels of their inheritance " +
468 "hierarchy. {2} does not implement Add({1}).", inheritFrom,
469 argumentType.FullName, type.FullName));
472 private static Hashtable keywordsTable;
473 private static string[] keywords = new string[] {
474 "abstract","event","new","struct","as","explicit","null","switch","base","extern",
475 "this","false","operator","throw","break","finally","out","true",
476 "fixed","override","try","case","params","typeof","catch","for",
477 "private","foreach","protected","checked","goto","public",
478 "unchecked","class","if","readonly","unsafe","const","implicit","ref",
479 "continue","in","return","using","virtual","default",
480 "interface","sealed","volatile","delegate","internal","do","is",
481 "sizeof","while","lock","stackalloc","else","static","enum",
483 "object","bool","byte","float","uint","char","ulong","ushort",
484 "decimal","int","sbyte","short","double","long","string","void",
486 "partial", "yield", "where"
491 internal static Type GetGenericListItemType (Type type)
493 if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type.GetGenericTypeDefinition ())) {
494 Type [] gatypes = type.GetGenericArguments ();
495 if (type.GetMethod ("Add", gatypes) != null)
499 foreach (Type i in type.GetInterfaces ())
500 if ((t = GetGenericListItemType (i)) != null)