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;
44 using XmlSchemaPatternFacet = System.Object;
47 namespace System.Xml.Serialization
49 internal class TypeData
58 string csharpFullName;
59 TypeData listItemTypeData;
60 TypeData listTypeData;
62 XmlSchemaPatternFacet facet;
63 bool hasPublicConstructor = true;
64 bool nullableOverride;
66 public TypeData (Type type, string elementName, bool isPrimitive) :
67 this(type, elementName, isPrimitive, null, null) {}
69 public TypeData (Type type, string elementName, bool isPrimitive, TypeData mappedType, XmlSchemaPatternFacet facet)
72 if (type.IsGenericTypeDefinition)
73 throw new InvalidOperationException ("Generic type definition cannot be used in serialization. Only specific generic types can be used.");
75 this.mappedType = mappedType;
78 this.typeName = type.Name;
79 this.fullTypeName = type.FullName.Replace ('+', '.');
82 sType = SchemaTypes.Primitive;
86 sType = SchemaTypes.Enum;
87 else if (typeof(IXmlSerializable).IsAssignableFrom (type))
88 sType = SchemaTypes.XmlSerializable;
90 else if (typeof (System.Xml.XmlNode).IsAssignableFrom (type))
91 sType = SchemaTypes.XmlNode;
93 else if (type.IsArray || typeof(IEnumerable).IsAssignableFrom (type))
94 sType = SchemaTypes.Array;
96 sType = SchemaTypes.Class;
100 this.elementName = TypeTranslator.GetArrayName (ListItemTypeData.XmlType);
102 this.elementName = elementName;
104 if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
105 hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
109 internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
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;
119 public string TypeName
126 public string XmlType
140 public string FullTypeName
147 public string CSharpName
150 if (csharpName == null)
151 csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
156 public string CSharpFullName
159 if (csharpFullName == null)
160 csharpFullName = (Type == null) ? TypeName : ToCSharpName (Type, true);
161 return csharpFullName;
165 // static Microsoft.CSharp.CSharpCodeProvider csprovider =
166 // new Microsoft.CSharp.CSharpCodeProvider ();
168 public static string ToCSharpName (Type type, bool full)
170 // return csprovider.GetTypeOutput (new System.CodeDom.CodeTypeReference (type));
171 StringBuilder sb = new StringBuilder ();
174 sb.Append (ToCSharpName (type.GetElementType (), full));
176 int rank = type.GetArrayRank ();
177 for (int i = 1; i < rank; i++)
180 return sb.ToString ();
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;
190 // Loop throguh the declaring class chain, consuming type arguments for every
191 // generic class in the chain
195 int i = pt.Name.IndexOf ('`');
197 int na = nt - int.Parse (pt.Name.Substring (i+1));
199 for (;nt > na; nt--) {
200 sb.Insert (0, ToCSharpName (args[nt],full));
205 sb.Insert (0, pt.Name.Substring (0, i));
207 sb.Insert (0, pt.Name);
208 pt = pt.DeclaringType;
210 if (full && type.Namespace.Length > 0)
211 sb.Insert (0, type.Namespace + ".");
212 return sb.ToString ();
215 if (type.DeclaringType != null) {
216 sb.Append (ToCSharpName (type.DeclaringType, full)).Append ('.');
217 sb.Append (type.Name);
220 if (full && type.Namespace.Length > 0)
221 sb.Append (type.Namespace).Append ('.');
222 sb.Append (type.Name);
224 return sb.ToString ();
227 static bool IsKeyword (string name)
229 if (keywordsTable == null) {
230 Hashtable t = new Hashtable ();
231 foreach (string s in keywords)
235 return keywordsTable.Contains (name);
238 public SchemaTypes SchemaType
245 public bool IsListType
247 get { return SchemaType == SchemaTypes.Array; }
250 public bool IsComplexType
254 return (SchemaType == SchemaTypes.Class ||
255 SchemaType == SchemaTypes.Array ||
256 SchemaType == SchemaTypes.Enum ||
257 SchemaType == SchemaTypes.XmlNode ||
258 SchemaType == SchemaTypes.XmlSerializable ||
263 public bool IsValueType
267 if (type != null) return type.IsValueType;
268 else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
272 public bool NullableOverride
274 get { return nullableOverride; }
277 public bool IsNullable
281 if (nullableOverride)
284 return !IsValueType ||
286 type.IsGenericType &&
287 type.GetGenericTypeDefinition () == typeof (Nullable<>));
295 nullableOverride = value;
299 public TypeData ListItemTypeData
303 if (listItemTypeData == null && type != null)
304 listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
305 return listItemTypeData;
309 public Type ListItemType
314 throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
316 if (listItemType != null) return listItemType;
318 Type genericArgument = null;
320 if (SchemaType != SchemaTypes.Array)
321 throw new InvalidOperationException (Type.FullName + " is not a collection");
322 else if (type.IsArray)
323 listItemType = type.GetElementType ();
325 else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
327 else if (typeof (ICollection).IsAssignableFrom (type))
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));
335 if (genericArgument != null)
336 listItemType = genericArgument;
338 PropertyInfo prop = GetIndexerProperty (type);
340 throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
341 listItemType = prop.PropertyType;
344 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
345 if (addMethod == null)
346 throw CreateMissingAddMethodException (type, "ICollection",
349 else // at this point, we must be dealing with IEnumerable implementation
351 MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
353 // get private implemenation
354 met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
355 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
356 null, Type.EmptyTypes, null);
358 // determine ListItemType using IEnumerator.Current property
359 PropertyInfo prop = met.ReturnType.GetProperty ("Current");
361 listItemType = typeof (object);
363 listItemType = prop.PropertyType;
365 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
366 if (addMethod == null)
367 throw CreateMissingAddMethodException (type, "IEnumerable",
375 public TypeData ListTypeData
379 if (listTypeData != null) return listTypeData;
381 listTypeData = new TypeData (TypeName + "[]",
383 TypeTranslator.GetArrayName(XmlType),
384 SchemaTypes.Array, this);
390 public bool IsXsdType {
391 get { return mappedType == null; }
394 public TypeData MappedType {
396 return mappedType != null ? mappedType : this;
400 public XmlSchemaPatternFacet XmlSchemaPatternFacet {
406 public bool HasPublicConstructor
408 get { return hasPublicConstructor; }
412 public static PropertyInfo GetIndexerProperty (Type collectionType)
414 PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
415 foreach (PropertyInfo prop in props)
417 ParameterInfo[] pi = prop.GetIndexParameters ();
418 if (pi != null && pi.Length == 1 && pi[0].ParameterType == typeof(int))
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));
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",
443 "object","bool","byte","float","uint","char","ulong","ushort",
444 "decimal","int","sbyte","short","double","long","string","void",
446 "partial", "yield", "where"
451 internal static Type GetGenericListItemType (Type type)
453 if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type.GetGenericTypeDefinition ())) {
454 Type [] gatypes = type.GetGenericArguments ();
455 if (type.GetMethod ("Add", gatypes) != null)
459 foreach (Type i in type.GetInterfaces ())
460 if ((t = GetGenericListItemType (i)) != null)