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