90654b04dd56ff91fb7223c91f24bec48fcbc280
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / XsdDataContractImporter.cs
1 //
2 // XsdDataContractImporter.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2010 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.CodeDom;
31 using System.CodeDom.Compiler;
32 using System.Collections.Generic;
33 using System.IO;
34 using System.Linq;
35 using System.Reflection;
36 using System.Xml;
37 using System.Xml.Schema;
38 using System.Xml.Serialization;
39
40 using QName = System.Xml.XmlQualifiedName;
41
42 namespace System.Runtime.Serialization
43 {
44         [MonoTODO ("support arrays")]
45         public class XsdDataContractImporter
46         {
47                 static readonly XmlQualifiedName qname_anytype = new XmlQualifiedName ("anyType", XmlSchema.Namespace);
48
49                 public XsdDataContractImporter ()
50                         : this (null)
51                 {
52                 }
53
54                 public XsdDataContractImporter (CodeCompileUnit codeCompileUnit)
55                 {
56                         // null argument is ok.
57                         CodeCompileUnit = codeCompileUnit ?? new CodeCompileUnit ();
58
59                         // Options is null by default
60                 }
61
62                 public CodeCompileUnit CodeCompileUnit { get; private set; }
63
64                 CodeDomProvider code_provider = CodeDomProvider.CreateProvider ("csharp");
65                 Dictionary<CodeNamespace,CodeIdentifiers> identifiers_table = new Dictionary<CodeNamespace,CodeIdentifiers> ();
66                 ImportOptions import_options;
67
68                 public ImportOptions Options {
69                         get { return import_options; }
70                         set {
71                                 import_options = value;
72                                 code_provider = value.CodeProvider ?? code_provider;
73                         }
74                 }
75
76                 void GenerateXmlType (XmlQualifiedName qname)
77                 {
78                         var cns = GetCodeNamespace (qname.Namespace);
79                         var td = new CodeTypeDeclaration () {
80                                 Name = GetUniqueName (CodeIdentifier.MakeValid (qname.Name), cns),
81                                 TypeAttributes = GenerateInternal ? TypeAttributes.NotPublic : TypeAttributes.Public };
82                         cns.Types.Add (td);
83                         td.BaseTypes.Add (new CodeTypeReference (typeof (IXmlSerializable)));
84
85                         var thisNodes = new CodePropertyReferenceExpression (new CodeThisReferenceExpression (), "Nodes"); // property this.Nodes
86                         var xmlSerializableServices = new CodeTypeReferenceExpression (typeof (XmlSerializableServices)); // static XmlSerializableServices.
87                         var qnameType = new CodeTypeReference (typeof (XmlQualifiedName));
88
89                         // XmlQualifiedName qname = new XmlQualifiedName ({qname.Name}, {qname.Namespace});
90                         td.Members.Add (new CodeMemberField () { Name = "qname", Type = qnameType, InitExpression = new CodeObjectCreateExpression (qnameType, new CodePrimitiveExpression (qname.Name), new CodePrimitiveExpression (qname.Namespace)) });
91
92                         // public XmlNode[] Nodes { get; set; }
93                         td.Members.Add (new CodeMemberProperty () { Name = "Nodes", Type = new CodeTypeReference (typeof (XmlNode [])), Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final, HasGet = true, HasSet = true });
94
95                         // public void ReadXml(XmlReader reader) {
96                         var read = new CodeMemberMethod () { Name = "ReadXml", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final };
97                         read.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlReader)), "reader"));
98                         //   this.Nodes = XmlSerializableServices.ReadXml(reader);
99                         read.Statements.Add (
100                                 new CodeAssignStatement (thisNodes,
101                                         new CodeMethodInvokeExpression (
102                                                 new CodeMethodReferenceExpression (xmlSerializableServices, "ReadXml"),
103                                                 new CodeArgumentReferenceExpression ("reader"))));
104                         // }
105                         td.Members.Add (read);
106
107                         // public void WriteXml(XmlWriter writer) {
108                         var write = new CodeMemberMethod () { Name = "WriteXml",Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final };
109                         write.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlWriter)), "writer"));
110                         //   XmlSerializableServices.WriteXml(writer, this.Nodes);
111                         write.Statements.Add (
112                                 new CodeMethodInvokeExpression (
113                                         new CodeMethodReferenceExpression (xmlSerializableServices, "WriteXml"),
114                                         new CodeArgumentReferenceExpression ("writer"),
115                                         thisNodes));
116                         // }
117                         td.Members.Add (write);
118
119                         // public XmlSchema GetSchema () { return null; }
120                         var getSchema = new CodeMemberMethod () { Name = "GetSchema", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final, ReturnType = new CodeTypeReference (typeof (XmlSchema)) };
121                         getSchema.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (null)));
122                         td.Members.Add (getSchema);
123
124                         // public static XmlQualifiedName ExportSchema (XmlSchemaSet schemas) {
125                         var export = new CodeMemberMethod () { Name = "ExportSchema", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final | MemberAttributes.Static, ReturnType = qnameType };
126                         export.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlSchemaSet)), "schemas"));
127                         //   XmlSerializableServices.AddDefaultSchema (schemas);
128                         export.Statements.Add (new CodeMethodInvokeExpression (xmlSerializableServices, "AddDefaultSchema", new CodeArgumentReferenceExpression ("schemas")));
129                         //   return qname;
130                         export.Statements.Add (new CodeMethodReturnStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "qname")));
131                         // }
132                         td.Members.Add (export);
133                 }
134
135                 // CanImport
136
137                 public bool CanImport (XmlSchemaSet schemas)
138                 {
139                         if (schemas == null)
140                                 throw new ArgumentNullException ("schemas");
141
142                         if (!schemas.IsCompiled)
143                                 schemas.Compile ();
144
145                         foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
146                                 if (!CanImport (schemas, xe))
147                                         return false;
148                         return true;
149                 }
150
151                 public bool CanImport (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
152                 {
153                         if (schemas == null)
154                                 throw new ArgumentNullException ("schemas");
155                         if (typeNames == null)
156                                 throw new ArgumentNullException ("typeNames");
157
158                         if (!schemas.IsCompiled)
159                                 schemas.Compile ();
160
161                         foreach (var name in typeNames)
162                                 if (!CanImport (schemas, name))
163                                         return false;
164                         return true;
165                 }
166
167                 public bool CanImport (XmlSchemaSet schemas, XmlQualifiedName typeName)
168                 {
169                         if (schemas == null)
170                                 throw new ArgumentNullException ("schemas");
171                         if (typeName == null)
172                                 throw new ArgumentNullException ("typeName");
173
174                         if (!schemas.IsCompiled)
175                                 schemas.Compile ();
176
177                         if (IsPredefinedType (typeName))
178                                 return true; // while it just ignores...
179
180                         if (!schemas.GlobalTypes.Contains (typeName))
181                                 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
182
183                         return CanImport (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType);
184                 }
185
186                 public bool CanImport (XmlSchemaSet schemas, XmlSchemaElement element)
187                 {
188                         if (schemas == null)
189                                 throw new ArgumentNullException ("schemas");
190                         if (element == null)
191                                 throw new ArgumentNullException ("element");
192
193                         if (!schemas.IsCompiled)
194                                 schemas.Compile ();
195
196                         return CanImport (schemas, element.ElementSchemaType as XmlSchemaType);
197                 }
198
199
200 #if true // new
201                 bool CanImport (XmlSchemaSet schemas, XmlSchemaType type)
202                 {
203                         if (IsPredefinedType (type.QualifiedName))
204                                 return true;
205
206                         var st = type as XmlSchemaSimpleType;
207                         if (st != null) {
208                                 return CanImportSimpleType (schemas, st);
209                         } else {
210                                 var ct = (XmlSchemaComplexType) type;
211                                 var sc = ct.ContentModel as XmlSchemaSimpleContent;
212                                 if (sc != null) {
213                                         if (sc.Content is XmlSchemaSimpleContentExtension)
214                                                 return false;
215                                 }
216                                 if (!CanImportComplexType (schemas, ct))
217                                         return false;
218                                 return true;
219                         }
220                 }
221
222                 bool CanImportSimpleType (XmlSchemaSet schemas, XmlSchemaSimpleType type)
223                 {
224                         var scl = type.Content as XmlSchemaSimpleTypeList;
225                         if (scl != null) {
226                                 if (scl.ItemType == null)
227                                         return false;
228                                 var itemType = scl.ItemType as XmlSchemaSimpleType;
229                                 var ir = itemType.Content as XmlSchemaSimpleTypeRestriction;
230                                 if (ir == null)
231                                         return false;
232                                 return true; // as enum
233                         }
234                         var scr = type.Content as XmlSchemaSimpleTypeRestriction;
235                         if (scr != null)
236                                 return true; // as enum
237
238                         return false;
239                 }
240
241                 bool CanImportComplexType (XmlSchemaSet schemas, XmlSchemaComplexType type)
242                 {
243                         foreach (XmlSchemaAttribute att in type.AttributeUses.Values)
244                                 if (att.Use != XmlSchemaUse.Optional || att.QualifiedName.Namespace != KnownTypeCollection.MSSimpleNamespace)
245                                         return false;
246
247                         CodeTypeReference baseClrType = null;
248                         var particle = type.Particle;
249                         if (type.ContentModel != null) {
250                                 var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction;
251                                 if (xsscr != null) {
252                                         if (xsscr.BaseType != null) {
253                                                 if (!CanImport (schemas, xsscr.BaseType))
254                                                         return false;
255                                         } else {
256                                                 if (!CanImport (schemas, xsscr.BaseTypeName))
257                                                         return false;
258                                         }
259                                         // The above will result in an error, but make sure to show we don't support it.
260                                         return false;
261                                 }
262                                 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
263                                 if (xscce != null) {
264                                         if (!CanImport (schemas, xscce.BaseTypeName))
265                                                 return false;
266                                         baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false);
267
268                                         var baseInfo = GetTypeInfo (xscce.BaseTypeName, false);
269                                         particle = xscce.Particle;
270                                 }
271                                 var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction;
272                                 if (xsccr != null)
273                                         return false;
274                         }
275
276                         var seq = particle as XmlSchemaSequence;
277                         if (seq == null && particle != null)
278                                 return false;
279
280                         if (seq != null) {
281
282                         if (seq.Items.Count == 1 && seq.Items [0] is XmlSchemaAny && type.Parent is XmlSchemaElement) {
283
284                                 // looks like it is not rejected (which contradicts the error message on .NET). See XsdDataContractImporterTest.ImportTestX32(). Also ImporTestX13() for Parent check.
285
286                         } else {
287
288                         foreach (var child in seq.Items)
289                                 if (!(child is XmlSchemaElement))
290                                         return false;
291
292                         bool isDictionary = false;
293                         if (type.Annotation != null) {
294                                 foreach (var ann in type.Annotation.Items) {
295                                         var ai = ann as XmlSchemaAppInfo;
296                                         if (ai != null && ai.Markup != null &&
297                                             ai.Markup.Length > 0 &&
298                                             ai.Markup [0].NodeType == XmlNodeType.Element &&
299                                             ai.Markup [0].LocalName == "IsDictionary" &&
300                                             ai.Markup [0].NamespaceURI == KnownTypeCollection.MSSimpleNamespace)
301                                                 isDictionary = true;
302                                 }
303                         }
304
305                         if (seq.Items.Count == 1) {
306                                 var pt = (XmlSchemaParticle) seq.Items [0];
307                                 var xe = pt as XmlSchemaElement;
308                                 if (pt.MaxOccursString == "unbounded") {
309                                         // import as a collection contract.
310                                         if (pt is XmlSchemaAny) {
311                                         } else if (isDictionary) {
312                                                 var kvt = xe.ElementSchemaType as XmlSchemaComplexType;
313                                                 var seq2 = kvt != null ? kvt.Particle as XmlSchemaSequence : null;
314                                                 var k = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [0] as XmlSchemaElement : null;
315                                                 var v = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [1] as XmlSchemaElement : null;
316                                                 if (k == null || v == null)
317                                                         return false;
318                                                 if (!CanImport (schemas, k.ElementSchemaType))
319                                                         return false;
320                                                 if (!CanImport (schemas, v.ElementSchemaType))
321                                                         return false;
322                                                 
323                                                 return true;
324                                         } else if (type.QualifiedName.Namespace == KnownTypeCollection.MSArraysNamespace &&
325                                                    IsPredefinedType (xe.ElementSchemaType.QualifiedName)) {
326                                                 // then this CodeTypeDeclaration is to be removed, and CodeTypeReference to this type should be an array instead.
327                                                 return true;
328                                         }
329                                         else
330                                                 if (!CanImport (schemas, xe.ElementSchemaType))
331                                                         return false;
332                                         return true;
333                                 }
334                         }
335                         if (isDictionary)
336                                 return false;
337
338                         // import as a (normal) contract.
339                         var elems = new List<XmlSchemaElement> ();
340                         foreach (XmlSchemaElement xe in seq.Items) {
341                                 if (xe.MaxOccurs != 1)
342                                         return false;
343
344                                 if (elems.Any (e => e.QualifiedName.Name == xe.QualifiedName.Name))
345                                         return false;
346
347                                 elems.Add (xe);
348                         }
349                         foreach (var xe in elems) {
350                                 // import property type in prior.
351                                 if (!CanImport (schemas, xe.ElementSchemaType.QualifiedName))
352                                         return false;
353                         }
354
355                         } // if (seq contains only an xs:any)
356                         } // if (seq != 0)
357
358                         return true;
359                 }
360 #else
361                 bool CanImport (XmlSchemaSet schemas, XmlSchemaComplexType type)
362                 {
363                         if (type == null || type.QualifiedName.Namespace == XmlSchema.Namespace) // xs:anyType -> not supported.
364                                 return false;
365
366                         if (type.ContentModel is XmlSchemaSimpleContent) // simple content derivation is not supported.
367                                 return false;
368                         if (type.ContentModel != null && type.ContentModel.Content != null) {
369                                 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
370                                 if (xscce == null) // complex DBR is not supported.
371                                         return false;
372                                 // check base type
373                                 if (xscce.BaseTypeName != qname_anytype && !CanImport (schemas, xscce.BaseTypeName))
374                                         return false;
375                         }
376
377                         return true;
378                 }
379 #endif
380
381                 // Import
382
383                 public void Import (XmlSchemaSet schemas)
384                 {
385                         if (schemas == null)
386                                 throw new ArgumentNullException ("schemas");
387
388                         if (!schemas.IsCompiled)
389                                 schemas.Compile ();
390
391                         foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
392                                 Import (schemas, xe);
393                 }
394
395                 public void Import (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
396                 {
397                         if (schemas == null)
398                                 throw new ArgumentNullException ("schemas");
399                         if (typeNames == null)
400                                 throw new ArgumentNullException ("typeNames");
401                         foreach (var name in typeNames)
402                                 Import (schemas, name);
403                 }
404
405                 // This checks type existence and raises an error if it is missing.
406                 public void Import (XmlSchemaSet schemas, XmlQualifiedName typeName)
407                 {
408                         if (schemas == null)
409                                 throw new ArgumentNullException ("schemas");
410                         if (typeName == null)
411                                 throw new ArgumentNullException ("typeName");
412
413                         if (IsPredefinedType (typeName))
414                                 return;
415
416                         if (!schemas.GlobalTypes.Contains (typeName))
417                                 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
418
419                         Import (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType, typeName);
420                 }
421
422                 public XmlQualifiedName Import (XmlSchemaSet schemas, XmlSchemaElement element)
423                 {
424                         if (schemas == null)
425                                 throw new ArgumentNullException ("schemas");
426                         if (element == null)
427                                 throw new ArgumentNullException ("element");
428
429                         var elname = element.QualifiedName;
430
431                         switch (elname.Namespace) {
432                         case KnownTypeCollection.MSSimpleNamespace:
433                                 switch (elname.Name) {
434                                 case "char":
435                                 case "duration":
436                                 case "guid":
437                                         return elname;
438                                 }
439                                 break;
440                         }
441
442                         if (!CanImport (schemas, element) && Options != null && Options.ImportXmlType) {
443                                 var qn = element.QualifiedName;
444                                 GenerateXmlType (qn);
445                                 return qn;
446                         }
447
448                         // FIXME: use element to fill nillable and arrays.
449                         var qname = element.SchemaType != null ? element.QualifiedName : element.ElementSchemaType.QualifiedName;
450                         Import (schemas, element.ElementSchemaType, qname);
451                         return qname;
452                 }
453
454                 void Import (XmlSchemaSet schemas, XmlSchemaType type)
455                 {
456                         if (!CanImport (schemas, type) && Options != null && Options.ImportXmlType) {
457                                 GenerateXmlType (type.QualifiedName);
458                                 return;
459                         }
460                         Import (schemas, type, type.QualifiedName);
461                 }
462
463                 void Import (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
464                 {
465                         var existing = imported_types.FirstOrDefault (it => it.XsdType == type);
466                         if (existing != null)
467                                 return;// existing.XsdTypeName;
468
469                         if (IsPredefinedType (type.QualifiedName))
470                                 return;
471
472                         DoImport (schemas, type, qname);
473                 }
474
475                 string GetUniqueName (string name, CodeNamespace cns)
476                 {
477                         CodeIdentifiers i;
478                         if (!identifiers_table.TryGetValue (cns, out i)) {
479                                 i = new CodeIdentifiers ();
480                                 identifiers_table.Add (cns, i);
481                         }
482                         return i.AddUnique (name, null);
483                 }
484
485                 void DoImport (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
486                 {
487                         CodeNamespace cns = null;
488                         CodeTypeReference clrRef;
489                         cns = GetCodeNamespace (qname.Namespace);
490                         clrRef = new CodeTypeReference (cns.Name.Length > 0 ? cns.Name + "." + qname.Name : qname.Name);
491
492                         var td = new CodeTypeDeclaration () {
493                                 Name = GetUniqueName (CodeIdentifier.MakeValid (qname.Name), cns),
494                                 TypeAttributes = GenerateInternal ? TypeAttributes.NotPublic : TypeAttributes.Public };
495                         cns.Types.Add (td);
496
497                         var info = new TypeImportInfo () { ClrType = clrRef, XsdType = type,  XsdTypeName = qname };
498                         imported_types.Add (info);
499
500                         var st = type as XmlSchemaSimpleType;
501                         if (st != null) {
502                                 ImportSimpleType (td, schemas, st, qname);
503                         } else {
504                                 var ct = (XmlSchemaComplexType) type;
505                                 var sc = ct.ContentModel as XmlSchemaSimpleContent;
506                                 if (sc != null) {
507                                         if (sc.Content is XmlSchemaSimpleContentExtension)
508                                                 throw new InvalidDataContractException (String.Format ("complex type '{0}' with simple content extension is not supported", type.QualifiedName));
509                                 }
510                                 if (!ImportComplexType (td, schemas, ct, qname)) {
511                                         cns.Types.Remove (td);
512                                         if (cns.Types.Count == 0)
513                                                 CodeCompileUnit.Namespaces.Remove (cns);
514                                 }
515
516                                 foreach (var impinfo in imported_types)
517                                         for (; impinfo.KnownTypeOutputIndex < impinfo.KnownClrTypes.Count; impinfo.KnownTypeOutputIndex++)
518                                                 td.CustomAttributes.Add (new CodeAttributeDeclaration (
519                                                         new CodeTypeReference (typeof (KnownTypeAttribute)),
520                                                         new CodeAttributeArgument (new CodeTypeOfExpression (impinfo.KnownClrTypes [impinfo.KnownTypeOutputIndex]))));
521                         }
522                 }
523
524                 static readonly string ass_name = typeof (DataContractAttribute).Assembly.GetName ().Name;
525                 static readonly string ass_version = typeof (DataContractAttribute).Assembly.GetName ().Version.ToString ();
526                 static readonly CodeTypeReference typeref_data_contract = new CodeTypeReference (typeof (DataContractAttribute));
527                 static readonly CodeTypeReference typeref_coll_contract = new CodeTypeReference (typeof (CollectionDataContractAttribute));
528
529                 void AddTypeAttributes (CodeTypeDeclaration td, XmlSchemaType type, params XmlSchemaElement [] collectionArgs)
530                 {
531                         var name = type.QualifiedName;
532                         // [GeneratedCode (assembly_name, assembly_version)]
533                         td.CustomAttributes.Add (new CodeAttributeDeclaration (
534                                 new CodeTypeReference (typeof (GeneratedCodeAttribute)),
535                                 new CodeAttributeArgument (new CodePrimitiveExpression (ass_name)),
536                                 new CodeAttributeArgument (new CodePrimitiveExpression (ass_version))));
537
538                         var ct = type as XmlSchemaComplexType;
539
540                         // [DataContract(Name="foobar",Namespace="urn:foobar")] (optionally IsReference=true),
541                         // or [CollectionDataContract(ditto, ItemType/KeyType/ValueType)]
542                         var dca = new CodeAttributeDeclaration (
543                                 collectionArgs != null && collectionArgs.Length > 0 ? typeref_coll_contract : typeref_data_contract,
544                                 new CodeAttributeArgument ("Name", new CodePrimitiveExpression (name.Name)),
545                                 new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (name.Namespace)));
546                         if (collectionArgs != null) {
547                                 if (collectionArgs.Length > 0)
548                                         dca.Arguments.Add (new CodeAttributeArgument ("ItemName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [0].QualifiedName.Name))));
549                                 if (collectionArgs.Length > 2) {
550                                         dca.Arguments.Add (new CodeAttributeArgument ("KeyName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [1].QualifiedName.Name))));
551                                         dca.Arguments.Add (new CodeAttributeArgument ("ValueName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [2].QualifiedName.Name))));
552                                 }
553                         }
554                         if (ct != null && ct.AttributeUses [new XmlQualifiedName ("Ref", KnownTypeCollection.MSSimpleNamespace)] != null)
555                                 dca.Arguments.Add (new CodeAttributeArgument ("IsReference", new CodePrimitiveExpression (true)));
556                         td.CustomAttributes.Add (dca);
557
558                         // optional [Serializable]
559                         if (Options != null && Options.GenerateSerializable)
560                                 td.CustomAttributes.Add (new CodeAttributeDeclaration ("System.SerializableAttribute"));
561                 }
562
563                 static readonly CodeTypeReference typeref_ext_iface = new CodeTypeReference ("System.Runtime.Serialization.IExtensibleDataObject");
564                 static readonly CodeTypeReference typeref_ext_class = new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject");
565
566                 void AddExtensionData (CodeTypeDeclaration td)
567                 {
568                         td.BaseTypes.Add (typeref_ext_iface);
569
570                         var field = new CodeMemberField (typeref_ext_class, "extensionDataField");
571                         td.Members.Add (field);
572
573                         var prop = new CodeMemberProperty () { Type = field.Type, Name = "ExtensionData", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final };
574                         prop.GetStatements.Add (new CodeMethodReturnStatement (
575                                 new CodeFieldReferenceExpression (
576                                 new CodeThisReferenceExpression (),
577                                 "extensionDataField")));
578                         prop.SetStatements.Add (new CodeAssignStatement (
579                                 new CodeFieldReferenceExpression (
580                                 new CodeThisReferenceExpression (),
581                                 "extensionDataField"),
582                                 new CodePropertySetValueReferenceExpression ()));
583
584                         td.Members.Add (prop);
585                 }
586
587                 void ImportSimpleType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleType type, XmlQualifiedName qname)
588                 {
589                         var scl = type.Content as XmlSchemaSimpleTypeList;
590                         if (scl != null) {
591                                 if (scl.ItemType == null)
592                                         throw new InvalidDataContractException (String.Format ("simple type list is allowed only with an anonymous simple type with enumeration restriction content as its item type definition (type is {0})", type.QualifiedName));
593                                 var itemType = scl.ItemType as XmlSchemaSimpleType;
594                                 var ir = itemType.Content as XmlSchemaSimpleTypeRestriction;
595                                 if (ir == null)
596                                         throw new InvalidDataContractException (String.Format ("simple type list is allowed only with an anonymous simple type with enumeration restriction content as its item type definition (type is {0})", type.QualifiedName));
597                                 ImportEnum (td, schemas, ir, type, qname, true);
598                                 return;
599                         }
600                         var scr = type.Content as XmlSchemaSimpleTypeRestriction;
601                         if (scr != null) {
602                                 ImportEnum (td, schemas, scr, type, qname, false);
603                                 return;
604                         }
605
606                         throw new InvalidDataContractException (String.Format ("simple type is supported only if it has enumeration or list of an anonymous simple type with enumeration restriction content as its item type definition (type is {0})", qname));
607                 }
608
609                 static readonly CodeTypeReference enum_member_att_ref = new CodeTypeReference (typeof (EnumMemberAttribute));
610
611                 void ImportEnum (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleTypeRestriction r, XmlSchemaType type, XmlQualifiedName qname, bool isFlag)
612                 {
613                         if (isFlag && !r.BaseTypeName.Equals (new XmlQualifiedName ("string", XmlSchema.Namespace)))
614                                 throw new InvalidDataContractException (String.Format ("For flags enumeration '{0}', the base type for the simple type restriction must be XML schema string", qname));
615
616                         td.IsEnum = true;
617                         AddTypeAttributes (td, type);
618                         if (isFlag)
619                                 td.CustomAttributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (typeof (FlagsAttribute))));
620
621                         foreach (var facet in r.Facets) {
622                                 var e = facet as XmlSchemaEnumerationFacet;
623                                 if (e == null)
624                                         throw new InvalidDataContractException (String.Format ("Invalid simple type restriction (type {0}). Only enumeration is allowed.", qname));
625                                 var em = new CodeMemberField () { Name = CodeIdentifier.MakeValid (e.Value) };
626                                 var ea = new CodeAttributeDeclaration (enum_member_att_ref);
627                                 if (e.Value != em.Name)
628                                         ea.Arguments.Add (new CodeAttributeArgument ("Value", new CodePrimitiveExpression (e.Value)));
629                                 em.CustomAttributes.Add (ea);
630                                 td.Members.Add (em);
631                         }
632                 }
633
634                 // Returns false if it should remove the imported type.
635                 bool ImportComplexType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaComplexType type, XmlQualifiedName qname)
636                 {
637                         foreach (XmlSchemaAttribute att in type.AttributeUses.Values)
638                                 if (att.Use != XmlSchemaUse.Optional || att.QualifiedName.Namespace != KnownTypeCollection.MSSimpleNamespace)
639                                         throw new InvalidDataContractException (String.Format ("attribute in DataContract complex type '{0}' is limited to those in {1} namespace, and optional.", qname, KnownTypeCollection.MSSimpleNamespace));
640
641                         CodeTypeReference baseClrType = null;
642                         var particle = type.Particle;
643                         if (type.ContentModel != null) {
644                                 var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction;
645                                 if (xsscr != null) {
646                                         if (xsscr.BaseType != null)
647                                                 Import (schemas, xsscr.BaseType);
648                                         else
649                                                 Import (schemas, xsscr.BaseTypeName);
650                                         // The above will result in an error, but make sure to show we don't support it.
651                                         throw new InvalidDataContractException (String.Format ("complex type simple content restriction is not supported in DataContract (type '{0}')", qname));
652                                 }
653                                 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
654                                 if (xscce != null) {
655                                         Import (schemas, xscce.BaseTypeName);
656                                         baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false);
657                                         if (baseClrType != null)
658                                                 td.BaseTypes.Add (baseClrType);
659
660                                         var baseInfo = GetTypeInfo (xscce.BaseTypeName, false);
661                                         if (baseInfo != null)
662                                                 baseInfo.KnownClrTypes.Add (imported_types.First (it => it.XsdType == type).ClrType);
663                                         particle = xscce.Particle;
664                                 }
665                                 var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction;
666                                 if (xsccr != null)
667                                         throw new InvalidDataContractException (String.Format ("complex content type (for type '{0}') has a restriction content model, which is not supported in DataContract.", qname));
668                         }
669
670                         var seq = particle as XmlSchemaSequence;
671                         if (seq == null && particle != null)
672                                 throw new InvalidDataContractException (String.Format ("Not supported particle {1}. In DataContract, only sequence particle is allowed as the top-level content of a complex type (type '{0}')", qname, particle));
673
674                         if (seq != null) {
675
676                         if (seq.Items.Count == 1 && seq.Items [0] is XmlSchemaAny && type.Parent is XmlSchemaElement) {
677
678                                 // looks like it is not rejected (which contradicts the error message on .NET). See XsdDataContractImporterTest.ImportTestX32(). Also ImporTestX13() for Parent check.
679
680                         } else {
681
682                         foreach (var child in seq.Items)
683                                 if (!(child is XmlSchemaElement))
684                                         throw new InvalidDataContractException (String.Format ("Only local element is allowed as the content of the sequence of the top-level content of a complex type '{0}'. Other particles (sequence, choice, all, any, group ref) are not supported.", qname));
685
686                         bool isDictionary = false;
687                         if (type.Annotation != null) {
688                                 foreach (var ann in type.Annotation.Items) {
689                                         var ai = ann as XmlSchemaAppInfo;
690                                         if (ai != null && ai.Markup != null &&
691                                             ai.Markup.Length > 0 &&
692                                             ai.Markup [0].NodeType == XmlNodeType.Element &&
693                                             ai.Markup [0].LocalName == "IsDictionary" &&
694                                             ai.Markup [0].NamespaceURI == KnownTypeCollection.MSSimpleNamespace)
695                                                 isDictionary = true;
696                                 }
697                         }
698
699                         if (seq.Items.Count == 1) {
700                                 var pt = (XmlSchemaParticle) seq.Items [0];
701                                 var xe = pt as XmlSchemaElement;
702                                 if (pt.MaxOccursString == "unbounded") {
703                                         // import as a collection contract.
704                                         if (pt is XmlSchemaAny) {
705                                         } else if (isDictionary) {
706                                                 var kvt = xe.ElementSchemaType as XmlSchemaComplexType;
707                                                 var seq2 = kvt != null ? kvt.Particle as XmlSchemaSequence : null;
708                                                 var k = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [0] as XmlSchemaElement : null;
709                                                 var v = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [1] as XmlSchemaElement : null;
710                                                 if (k == null || v == null)
711                                                         throw new InvalidDataContractException (String.Format ("Invalid Dictionary contract type '{0}'. A Dictionary schema type must have a sequence particle which contains exactly two schema elements for key and value.", type.QualifiedName));
712                                                 Import (schemas, k.ElementSchemaType);
713                                                 Import (schemas, v.ElementSchemaType);
714                                                 td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.Dictionary", GetCodeTypeReference (k.ElementSchemaType.QualifiedName), GetCodeTypeReference (v.ElementSchemaType.QualifiedName)));
715                                                 AddTypeAttributes (td, type, xe, k, v);
716                                                 return true;
717                                         } else if (type.QualifiedName.Namespace == KnownTypeCollection.MSArraysNamespace &&
718                                                    IsPredefinedType (xe.ElementSchemaType.QualifiedName)) {
719                                                 // then this CodeTypeDeclaration is to be removed, and CodeTypeReference to this type should be an array instead.
720                                                 var cti = imported_types.First (i => i.XsdType == type);
721                                                 cti.ClrType = new CodeTypeReference (GetCodeTypeReference (xe.ElementSchemaType.QualifiedName), 1);
722                                         
723                                                 return false;
724                                         }
725                                         else
726                                                 Import (schemas, xe.ElementSchemaType);
727                                         td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.List", GetCodeTypeReference (xe.ElementSchemaType.QualifiedName)));
728                                         AddTypeAttributes (td, type, xe);
729                                         return true;
730                                 }
731                         }
732                         if (isDictionary)
733                                 throw new InvalidDataContractException (String.Format ("complex type '{0}' is an invalid Dictionary type definition. A Dictionary must have a sequence particle with exactly two child elements", qname));
734
735                         // import as a (normal) contract.
736                         var elems = new List<XmlSchemaElement> ();
737                         foreach (XmlSchemaElement xe in seq.Items) {
738                                 if (xe.MaxOccurs != 1)
739                                         throw new InvalidDataContractException (String.Format ("schema complex type '{0}' has a content sequence containing an element '{1}' with 'maxOccurs' value as more than 1, which is not supported in DataContract.", qname, xe.QualifiedName));
740
741                                 if (elems.Any (e => e.QualifiedName.Name == xe.QualifiedName.Name))
742                                         throw new InvalidDataContractException (String.Format ("In schema type '{0}', there already is an element whose name is {1}, where duplicate of element names are not supported.", qname, xe.QualifiedName.Name));
743
744                                 elems.Add (xe);
745                         }
746                         foreach (var xe in elems) {
747                                 // import property type in prior.
748                                 Import (schemas, xe.ElementSchemaType.QualifiedName);
749                                 AddProperty (td, xe);
750                         }
751
752                         } // if (seq contains only an xs:any)
753                         } // if (seq != 0)
754
755                         AddTypeAttributes (td, type);
756                         AddExtensionData (td);
757
758                         return true;
759                 }
760
761                 static readonly CodeExpression this_expr = new CodeThisReferenceExpression ();
762                 static readonly CodeExpression arg_value_expr = new CodePropertySetValueReferenceExpression ();
763
764                 bool GenerateInternal {
765                         get { return Options != null && Options.GenerateInternal; }
766                 }
767
768                 void AddProperty (CodeTypeDeclaration td, XmlSchemaElement xe)
769                 {
770                         var att = GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public;
771                         var fi = new CodeMemberField () { Name = CodeIdentifier.MakeValid (xe.QualifiedName.Name + "Field"), Type = GetCodeTypeReference (xe.ElementSchemaType.QualifiedName, xe) };
772                         td.Members.Add (fi);
773                         var pi = new CodeMemberProperty () { Name = xe.QualifiedName.Name, Attributes = att, HasGet = true, HasSet = true, Type = fi.Type };
774                         // [DataMember(Name=foobar, IsRequired=!nillable)]
775                         var dma = new CodeAttributeDeclaration (
776                                 new CodeTypeReference (typeof (DataMemberAttribute)));
777                         if (fi.Name != xe.QualifiedName.Name)
778                                 new CodeAttributeArgument ("Name", new CodePrimitiveExpression (xe.QualifiedName.Name));
779                         if (!xe.IsNillable)
780                                 new CodeAttributeArgument ("IsRequired", new CodePrimitiveExpression (true));
781                         pi.CustomAttributes.Add (dma);
782
783                         pi.GetStatements.Add (new CodeMethodReturnStatement () { Expression = new CodeFieldReferenceExpression (this_expr, fi.Name) });
784                         pi.SetStatements.Add (new CodeAssignStatement (new CodeFieldReferenceExpression (this_expr, fi.Name), arg_value_expr));
785
786
787                         td.Members.Add (pi);
788                 }
789
790                 bool IsPredefinedType (XmlQualifiedName qname)
791                 {
792                         switch (qname.Namespace) {
793                         case KnownTypeCollection.MSSimpleNamespace:
794                                 return KnownTypeCollection.GetPrimitiveTypeFromName (qname) != null;
795                         case XmlSchema.Namespace:
796                                 return XmlSchemaType.GetBuiltInSimpleType (qname) != null || XmlSchemaType.GetBuiltInComplexType (qname) != null;
797                         }
798                         return false;
799                 }
800
801                 CodeNamespace GetCodeNamespace (string xmlns)
802                 {
803                         string ns = null;
804                         if (Options == null || !Options.Namespaces.TryGetValue (xmlns, out ns))
805                                 ns = GetCodeNamespaceFromXmlns (xmlns);
806
807                         foreach (CodeNamespace cns in CodeCompileUnit.Namespaces)
808                                 if (cns.Name == ns)
809                                         return cns;
810                         var newCns = new CodeNamespace () { Name = ns };
811                         CodeCompileUnit.Namespaces.Add (newCns);
812                         return newCns;
813                 }
814
815                 const string default_ns_prefix = "http://schemas.datacontract.org/2004/07/";
816
817                 string GetCodeNamespaceFromXmlns (string xns)
818                 {
819                         if (xns.StartsWith (default_ns_prefix, StringComparison.Ordinal))
820                                 xns = xns.Substring (default_ns_prefix.Length);
821                         else {
822                                 Uri u;
823                                 string tmp;
824                                 if (Uri.TryCreate (xns, UriKind.Absolute, out u) && (tmp = MakeStringNamespaceComponentsValid (u.GetComponents (UriComponents.Host | UriComponents.Path, UriFormat.Unescaped))).Length > 0)
825                                         xns = tmp;
826                         }
827                         return MakeStringNamespaceComponentsValid (xns);
828                 }
829
830                 static readonly char [] split_tokens = new char [] {'/', '.'};
831
832                 string MakeStringNamespaceComponentsValid (string ns)
833                 {
834                         var arr = ns.Split (split_tokens, StringSplitOptions.RemoveEmptyEntries);
835                         for (int i = 0; i < arr.Length; i++)
836                                 arr [i] = CodeIdentifier.MakeValid (arr [i]);
837                         return String.Join (".", arr);
838                 }
839
840                 // Post-compilation information retrieval
841
842                 TypeImportInfo GetTypeInfo (XmlQualifiedName typeName, bool throwError)
843                 {
844                         var info = imported_types.FirstOrDefault (i => i.XsdTypeName.Equals (typeName));
845                         if (info == null) {
846                                 if (throwError)
847                                         throw new InvalidOperationException (String.Format ("schema type '{0}' has not been imported yet. Import it first.", typeName));
848                                 return null;
849                         }
850                         return info;
851                 }
852
853                 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName)
854                 {
855                         return GetCodeTypeReferenceInternal (typeName, true);
856                 }
857
858                 CodeTypeReference GetCodeTypeReferenceInternal (XmlQualifiedName typeName, bool throwError)
859                 {
860                         if (typeName == null)
861                                 throw new ArgumentNullException ("typeName");
862
863                         switch (typeName.Namespace) {
864                         case XmlSchema.Namespace:
865                         case KnownTypeCollection.MSSimpleNamespace:
866                                 var pt = KnownTypeCollection.GetPrimitiveTypeFromName (typeName);
867                                 if (pt == null)
868                                         throw new ArgumentException (String.Format ("Invalid type name in a predefined namespace: {0}", typeName));
869                                 return new CodeTypeReference (pt);
870                         }
871
872                         var info = GetTypeInfo (typeName, throwError);
873                         return info != null ? info.ClrType : null;
874                 }
875
876                 [MonoTODO ("use element argument and fill Nullable etc.")]
877                 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName, XmlSchemaElement element)
878                 {
879                         if (typeName == null)
880                                 throw new ArgumentNullException ("typeName");
881                         if (element == null)
882                                 throw new ArgumentNullException ("element");
883
884                         return GetCodeTypeReference (typeName);
885                 }
886
887                 public ICollection<CodeTypeReference> GetKnownTypeReferences (XmlQualifiedName typeName)
888                 {
889                         if (typeName == null)
890                                 throw new ArgumentNullException ("typeName");
891
892                         return GetTypeInfo (typeName, true).KnownClrTypes;
893                 }
894
895                 List<TypeImportInfo> imported_types = new List<TypeImportInfo> ();
896
897                 class TypeImportInfo
898                 {
899                         public TypeImportInfo ()
900                         {
901                                 KnownClrTypes = new List<CodeTypeReference> ();
902                         }
903
904                         public CodeTypeReference ClrType { get; set; }
905                         public XmlQualifiedName XsdTypeName { get; set; }
906                         public XmlSchemaType XsdType { get; set; }
907                         public List<CodeTypeReference> KnownClrTypes { get; private set; }
908                         public int KnownTypeOutputIndex { get; set; } // updated while importing.
909                 }
910         }
911 }