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;
140 void LookupTypeConvertor ()
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);
150 internal void ConvertForAssignment (ref object value)
152 // Has this object registered a custom type converter?
153 if (typeConvertor != null)
154 value = typeConvertor.Invoke (null, new object[] { value });
157 public string TypeName
164 public string XmlType
178 public string FullTypeName
185 public string CSharpName
188 if (csharpName == null)
189 csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
194 public string CSharpFullName
197 if (csharpFullName == null)
198 csharpFullName = (Type == null) ? TypeName : ToCSharpName (Type, true);
199 return csharpFullName;
203 // static Microsoft.CSharp.CSharpCodeProvider csprovider =
204 // new Microsoft.CSharp.CSharpCodeProvider ();
206 public static string ToCSharpName (Type type, bool full)
208 // return csprovider.GetTypeOutput (new System.CodeDom.CodeTypeReference (type));
209 StringBuilder sb = new StringBuilder ();
212 sb.Append (ToCSharpName (type.GetElementType (), full));
214 int rank = type.GetArrayRank ();
215 for (int i = 1; i < rank; i++)
218 return sb.ToString ();
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;
228 // Loop throguh the declaring class chain, consuming type arguments for every
229 // generic class in the chain
233 int i = pt.Name.IndexOf ('`');
235 int na = nt - int.Parse (pt.Name.Substring (i+1));
237 for (;nt > na; nt--) {
238 sb.Insert (0, ToCSharpName (args[nt],full));
243 sb.Insert (0, pt.Name.Substring (0, i));
245 sb.Insert (0, pt.Name);
246 pt = pt.DeclaringType;
248 if (full && type.Namespace.Length > 0)
249 sb.Insert (0, type.Namespace + ".");
250 return sb.ToString ();
253 if (type.DeclaringType != null) {
254 sb.Append (ToCSharpName (type.DeclaringType, full)).Append ('.');
255 sb.Append (type.Name);
258 if (full && type.Namespace.Length > 0)
259 sb.Append (type.Namespace).Append ('.');
260 sb.Append (type.Name);
262 return sb.ToString ();
265 static bool IsKeyword (string name)
267 if (keywordsTable == null) {
268 Hashtable t = new Hashtable ();
269 foreach (string s in keywords)
273 return keywordsTable.Contains (name);
276 public SchemaTypes SchemaType
283 public bool IsListType
285 get { return SchemaType == SchemaTypes.Array; }
288 public bool IsComplexType
292 return (SchemaType == SchemaTypes.Class ||
293 SchemaType == SchemaTypes.Array ||
294 SchemaType == SchemaTypes.Enum ||
295 SchemaType == SchemaTypes.XmlNode ||
296 SchemaType == SchemaTypes.XmlSerializable ||
301 public bool IsValueType
305 if (type != null) return type.IsValueType;
306 else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
310 public bool NullableOverride
312 get { return nullableOverride; }
315 public bool IsNullable
319 if (nullableOverride)
322 return !IsValueType ||
324 type.IsGenericType &&
325 type.GetGenericTypeDefinition () == typeof (Nullable<>));
333 nullableOverride = value;
337 public TypeData ListItemTypeData
341 if (listItemTypeData == null && type != null)
342 listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
343 return listItemTypeData;
347 public Type ListItemType
352 throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
354 if (listItemType != null) return listItemType;
356 Type genericArgument = null;
358 if (SchemaType != SchemaTypes.Array)
359 throw new InvalidOperationException (Type.FullName + " is not a collection");
360 else if (type.IsArray)
361 listItemType = type.GetElementType ();
363 else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
365 else if (typeof (ICollection).IsAssignableFrom (type))
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));
373 if (genericArgument != null)
374 listItemType = genericArgument;
376 PropertyInfo prop = GetIndexerProperty (type);
378 throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
379 listItemType = prop.PropertyType;
382 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
383 if (addMethod == null)
384 throw CreateMissingAddMethodException (type, "ICollection",
387 else // at this point, we must be dealing with IEnumerable implementation
389 MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
391 // get private implemenation
392 met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
393 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
394 null, Type.EmptyTypes, null);
396 // determine ListItemType using IEnumerator.Current property
397 PropertyInfo prop = met.ReturnType.GetProperty ("Current");
399 listItemType = typeof (object);
401 listItemType = prop.PropertyType;
403 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
404 if (addMethod == null)
405 throw CreateMissingAddMethodException (type, "IEnumerable",
413 public TypeData ListTypeData
417 if (listTypeData != null) return listTypeData;
419 listTypeData = new TypeData (TypeName + "[]",
421 TypeTranslator.GetArrayName(XmlType),
422 SchemaTypes.Array, this);
428 public bool IsXsdType {
429 get { return mappedType == null; }
432 public TypeData MappedType {
434 return mappedType != null ? mappedType : this;
438 public XmlSchemaPatternFacet XmlSchemaPatternFacet {
444 public bool HasPublicConstructor
446 get { return hasPublicConstructor; }
450 public static PropertyInfo GetIndexerProperty (Type collectionType)
452 PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
453 foreach (PropertyInfo prop in props)
455 ParameterInfo[] pi = prop.GetIndexParameters ();
456 if (pi != null && pi.Length == 1 && pi[0].ParameterType == typeof(int))
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));
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",
481 "object","bool","byte","float","uint","char","ulong","ushort",
482 "decimal","int","sbyte","short","double","long","string","void",
484 "partial", "yield", "where"
489 internal static Type GetGenericListItemType (Type type)
491 if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type.GetGenericTypeDefinition ())) {
492 Type [] gatypes = type.GetGenericArguments ();
493 if (type.GetMethod ("Add", gatypes) != null)
497 foreach (Type i in type.GetInterfaces ())
498 if ((t = GetGenericListItemType (i)) != null)