New test.
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / TypeData.cs
1 //
2 // System.Xml.Serialization.TypeData
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //  Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // (C) 2002 Ximian, Inc (http://www.ximian.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections;
34 using System.Globalization;
35 using System.Reflection;
36 using System.Xml.Schema;
37
38 namespace System.Xml.Serialization
39 {
40         internal class TypeData
41         {
42                 Type type;
43                 string elementName;
44                 SchemaTypes sType;
45                 Type listItemType;
46                 string typeName;
47                 string fullTypeName;
48                 TypeData listItemTypeData;
49                 TypeData listTypeData;
50                 TypeData mappedType;
51                 XmlSchemaPatternFacet facet;
52                 bool hasPublicConstructor = true;
53
54                 public TypeData (Type type, string elementName, bool isPrimitive) :
55                         this(type, elementName, isPrimitive, null, null) {}
56
57                 public TypeData (Type type, string elementName, bool isPrimitive, TypeData mappedType, XmlSchemaPatternFacet facet)
58                 {
59                         this.mappedType = mappedType;
60                         this.facet = facet;
61                         this.type = type;
62                         this.typeName = type.Name;
63                         this.fullTypeName = type.FullName.Replace ('+', '.');
64
65                         if (isPrimitive)
66                                 sType = SchemaTypes.Primitive;
67                         else
68                         {
69                                 if (type.IsEnum)
70                                         sType = SchemaTypes.Enum;
71                                 else if (typeof(IXmlSerializable).IsAssignableFrom (type))
72                                         sType = SchemaTypes.XmlSerializable;
73                                 else if (typeof (System.Xml.XmlNode).IsAssignableFrom (type))
74                                         sType = SchemaTypes.XmlNode;
75                                 else if (type.IsArray || typeof(IEnumerable).IsAssignableFrom (type))
76                                         sType = SchemaTypes.Array;
77                                 else
78                                         sType = SchemaTypes.Class;
79                         }
80                         
81                         if (IsListType)
82                                 this.elementName = TypeTranslator.GetArrayName (ListItemTypeData.XmlType);
83                         else
84                                 this.elementName = elementName;
85
86                         if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
87                                 hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
88                         }
89                 }
90
91                 internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
92                 {
93                         this.elementName = xmlType;
94                         this.typeName = typeName;
95                         this.fullTypeName = fullTypeName.Replace ('+', '.');
96                         this.listItemTypeData = listItemTypeData;
97                         this.sType = schemaType;
98                         this.hasPublicConstructor = true;
99                 }
100
101                 public string TypeName
102                 {
103                         get {
104                                 return typeName;
105                         }
106                 }
107                                 
108                 public string XmlType
109                 {
110                         get {
111                                 return elementName;
112                         }
113                 }
114                                 
115                 public Type Type
116                 {
117                         get {
118                                 return type;
119                         }
120                 }
121                                 
122                 public string FullTypeName
123                 {
124                         get {
125                                 return fullTypeName;
126                         }
127                 }
128
129                 public SchemaTypes SchemaType
130                 {
131                         get {
132                                 return sType;
133                         }
134                 }
135
136                 public bool IsListType
137                 {
138                         get { return SchemaType == SchemaTypes.Array; }
139                 }
140
141                 public bool IsComplexType
142                 {
143                         get 
144                         { 
145                                 return (SchemaType == SchemaTypes.Class || 
146                                               SchemaType == SchemaTypes.Array ||
147                                               SchemaType == SchemaTypes.Enum ||
148                                               SchemaType == SchemaTypes.XmlNode ||
149                                                   SchemaType == SchemaTypes.XmlSerializable ||
150                                                   !IsXsdType); 
151                         }
152                 }
153
154                 public bool IsValueType
155                 {
156                         get
157                         {
158                                 if (type != null) return type.IsValueType;
159                                 else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
160                         }
161                 }
162
163                 public TypeData ListItemTypeData
164                 {
165                         get
166                         {
167                                 if (listItemTypeData == null && type != null)
168                                         listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
169                                 return listItemTypeData;
170                         }
171                 }
172                 
173                 public Type ListItemType
174                 {
175                         get
176                         {
177                                 if (type == null) 
178                                         throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
179
180                                 if (listItemType != null) return listItemType;
181
182                                 if (SchemaType != SchemaTypes.Array)
183                                         throw new InvalidOperationException (Type.FullName + " is not a collection");
184                                 else if (type.IsArray) 
185                                         listItemType = type.GetElementType ();
186                                 else if (typeof(ICollection).IsAssignableFrom (type))
187                                 {
188                                         if (typeof (IDictionary).IsAssignableFrom (type))
189                                                 throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture,
190                                                         "The type {0} is not supported because it implements" +
191                                                         " IDictionary.", type.FullName));
192
193                                         PropertyInfo prop = GetIndexerProperty (type);
194                                         if (prop == null) 
195                                                 throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
196
197                                         listItemType = prop.PropertyType;
198
199                                         MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
200                                         if (addMethod == null)
201                                                 throw CreateMissingAddMethodException (type, "ICollection",
202                                                         listItemType);
203                                 }
204                                 else // at this point, we must be dealing with IEnumerable implementation
205                                 {
206                                         MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
207                                         if (met == null) { 
208                                                 // get private implemenation
209                                                 met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
210                                                         BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
211                                                         null, Type.EmptyTypes, null);
212                                         }
213                                         // determine ListItemType using IEnumerator.Current property
214                                         PropertyInfo prop = met.ReturnType.GetProperty ("Current");
215                                         if (prop == null)
216                                                 listItemType = typeof (object);
217                                         else
218                                                 listItemType = prop.PropertyType;
219
220                                         MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
221                                         if (addMethod == null)
222                                                 throw CreateMissingAddMethodException (type, "IEnumerable",
223                                                         listItemType);
224                                 }
225
226                                 return listItemType;
227                         }
228                 }
229
230                 public TypeData ListTypeData
231                 {
232                         get
233                         {
234                                 if (listTypeData != null) return listTypeData;
235                                 
236                                 listTypeData = new TypeData (TypeName + "[]",
237                                         FullTypeName + "[]",
238                                         TypeTranslator.GetArrayName(XmlType),
239                                         SchemaTypes.Array, this);
240
241                                 return listTypeData;
242                         }
243                 }
244
245                 public bool IsXsdType {
246                         get { return mappedType == null; }
247                 }
248
249                 public TypeData MappedType {
250                         get {
251                                 return mappedType != null ? mappedType : this;
252                         }
253                 }
254
255                 public XmlSchemaPatternFacet XmlSchemaPatternFacet {
256                         get {
257                                 return facet;
258                         }
259                 }
260                 
261                 public bool HasPublicConstructor
262                 {
263                         get { return hasPublicConstructor; }
264                 }
265
266
267                 public static PropertyInfo GetIndexerProperty (Type collectionType)
268                 {
269                         PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
270                         foreach (PropertyInfo prop in props)
271                         {
272                                 ParameterInfo[] pi = prop.GetIndexParameters ();
273                                 if (pi != null && pi.Length == 1 && pi[0].ParameterType == typeof(int))
274                                         return prop;
275                         }
276                         return null;
277                 }
278
279                 private static InvalidOperationException CreateMissingAddMethodException (Type type, string inheritFrom, Type argumentType) {
280                         return new InvalidOperationException (string.Format(CultureInfo.InvariantCulture,
281                                 "To be XML serializable, types which inherit from {0} must have " +
282                                 "an implementation of Add({1}) at all levels of their inheritance " +
283                                 "hierarchy. {2} does not implement Add({1}).", inheritFrom, 
284                                 argumentType.FullName, type.FullName));
285                 }
286         }
287 }