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 internal class TypeData
54 string csharpFullName;
55 TypeData listItemTypeData;
56 TypeData listTypeData;
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)
68 if (type.IsGenericTypeDefinition)
69 throw new InvalidOperationException ("Generic type definition cannot be used in serialization. Only specific generic types can be used.");
71 this.mappedType = mappedType;
74 this.typeName = type.Name;
75 this.fullTypeName = type.FullName.Replace ('+', '.');
78 sType = SchemaTypes.Primitive;
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;
90 sType = SchemaTypes.Class;
94 this.elementName = TypeTranslator.GetArrayName (ListItemTypeData.XmlType);
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
120 public string XmlType
134 public string FullTypeName
141 public string CSharpName
144 if (csharpName == null)
145 csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
150 public string CSharpFullName
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 StringBuilder sb = new StringBuilder ();
168 sb.Append (ToCSharpName (type.GetElementType (), full));
170 int rank = type.GetArrayRank ();
171 for (int i = 1; i < rank; i++)
174 return sb.ToString ();
177 // Generic nested types return the complete list of type arguments,
178 // including type arguments for the declaring class. This requires
179 // some special handling
180 if (type.IsGenericType && !type.IsGenericTypeDefinition) {
181 Type[] args = type.GetGenericArguments ();
182 int nt = args.Length - 1;
184 // Loop throguh the declaring class chain, consuming type arguments for every
185 // generic class in the chain
189 int i = pt.Name.IndexOf ('`');
191 int na = nt - int.Parse (pt.Name.Substring (i+1));
193 for (;nt > na; nt--) {
194 sb.Insert (0, ToCSharpName (args[nt],full));
199 sb.Insert (0, pt.Name.Substring (0, i));
201 sb.Insert (0, pt.Name);
202 pt = pt.DeclaringType;
204 if (full && type.Namespace.Length > 0)
205 sb.Insert (0, type.Namespace + ".");
206 return sb.ToString ();
209 if (type.DeclaringType != null) {
210 sb.Append (ToCSharpName (type.DeclaringType, full)).Append ('.');
211 sb.Append (type.Name);
214 if (full && type.Namespace.Length > 0)
215 sb.Append (type.Namespace).Append ('.');
216 sb.Append (type.Name);
218 return sb.ToString ();
221 static bool IsKeyword (string name)
223 if (keywordsTable == null) {
224 Hashtable t = new Hashtable ();
225 foreach (string s in keywords)
229 return keywordsTable.Contains (name);
232 public SchemaTypes SchemaType
239 public bool IsListType
241 get { return SchemaType == SchemaTypes.Array; }
244 public bool IsComplexType
248 return (SchemaType == SchemaTypes.Class ||
249 SchemaType == SchemaTypes.Array ||
250 SchemaType == SchemaTypes.Enum ||
251 SchemaType == SchemaTypes.XmlNode ||
252 SchemaType == SchemaTypes.XmlSerializable ||
257 public bool IsValueType
261 if (type != null) return type.IsValueType;
262 else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
266 public bool NullableOverride
268 get { return nullableOverride; }
271 public bool IsNullable
275 if (nullableOverride)
278 return !IsValueType ||
280 type.IsGenericType &&
281 type.GetGenericTypeDefinition () == typeof (Nullable<>));
289 nullableOverride = value;
293 public TypeData ListItemTypeData
297 if (listItemTypeData == null && type != null)
298 listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
299 return listItemTypeData;
303 public Type ListItemType
308 throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
310 if (listItemType != null) return listItemType;
312 Type genericArgument = null;
314 if (SchemaType != SchemaTypes.Array)
315 throw new InvalidOperationException (Type.FullName + " is not a collection");
316 else if (type.IsArray)
317 listItemType = type.GetElementType ();
319 else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
321 else if (typeof (ICollection).IsAssignableFrom (type))
324 if (typeof (IDictionary).IsAssignableFrom (type))
325 throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture,
326 "The type {0} is not supported because it implements" +
327 " IDictionary.", type.FullName));
329 if (genericArgument != null)
330 listItemType = genericArgument;
332 PropertyInfo prop = GetIndexerProperty (type);
334 throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
335 listItemType = prop.PropertyType;
338 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
339 if (addMethod == null)
340 throw CreateMissingAddMethodException (type, "ICollection",
343 else // at this point, we must be dealing with IEnumerable implementation
345 MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
347 // get private implemenation
348 met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
349 BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
350 null, Type.EmptyTypes, null);
352 // determine ListItemType using IEnumerator.Current property
353 PropertyInfo prop = met.ReturnType.GetProperty ("Current");
355 listItemType = typeof (object);
357 listItemType = prop.PropertyType;
359 MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
360 if (addMethod == null)
361 throw CreateMissingAddMethodException (type, "IEnumerable",
369 public TypeData ListTypeData
373 if (listTypeData != null) return listTypeData;
375 listTypeData = new TypeData (TypeName + "[]",
377 TypeTranslator.GetArrayName(XmlType),
378 SchemaTypes.Array, this);
384 public bool IsXsdType {
385 get { return mappedType == null; }
388 public TypeData MappedType {
390 return mappedType != null ? mappedType : this;
394 public XmlSchemaPatternFacet XmlSchemaPatternFacet {
400 public bool HasPublicConstructor
402 get { return hasPublicConstructor; }
406 public static PropertyInfo GetIndexerProperty (Type collectionType)
408 PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
409 foreach (PropertyInfo prop in props)
411 ParameterInfo[] pi = prop.GetIndexParameters ();
412 if (pi != null && pi.Length == 1 && pi[0].ParameterType == typeof(int))
418 private static InvalidOperationException CreateMissingAddMethodException (Type type, string inheritFrom, Type argumentType) {
419 return new InvalidOperationException (string.Format(CultureInfo.InvariantCulture,
420 "To be XML serializable, types which inherit from {0} must have " +
421 "an implementation of Add({1}) at all levels of their inheritance " +
422 "hierarchy. {2} does not implement Add({1}).", inheritFrom,
423 argumentType.FullName, type.FullName));
426 private static Hashtable keywordsTable;
427 private static string[] keywords = new string[] {
428 "abstract","event","new","struct","as","explicit","null","switch","base","extern",
429 "this","false","operator","throw","break","finally","out","true",
430 "fixed","override","try","case","params","typeof","catch","for",
431 "private","foreach","protected","checked","goto","public",
432 "unchecked","class","if","readonly","unsafe","const","implicit","ref",
433 "continue","in","return","using","virtual","default",
434 "interface","sealed","volatile","delegate","internal","do","is",
435 "sizeof","while","lock","stackalloc","else","static","enum",
437 "object","bool","byte","float","uint","char","ulong","ushort",
438 "decimal","int","sbyte","short","double","long","string","void",
440 "partial", "yield", "where"
445 private Type GetGenericListItemType (Type type)
447 if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (ICollection<>))
448 return type.GetGenericArguments () [0];
450 foreach (Type i in type.GetInterfaces ())
451 if ((t = GetGenericListItemType (i)) != null)