Make generated type names unique.
[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 namespace System.Runtime.Serialization
41 {
42         [MonoTODO ("Support ImportXmlType option; support arrays; CanImport is not up to date with Import")]
43         public class XsdDataContractImporter
44         {
45                 static readonly XmlQualifiedName qname_anytype = new XmlQualifiedName ("anyType", XmlSchema.Namespace);
46
47                 public XsdDataContractImporter ()
48                         : this (null)
49                 {
50                 }
51
52                 public XsdDataContractImporter (CodeCompileUnit codeCompileUnit)
53                 {
54                         // null argument is ok.
55                         CodeCompileUnit = codeCompileUnit ?? new CodeCompileUnit ();
56
57                         // Options is null by default
58                 }
59
60                 public CodeCompileUnit CodeCompileUnit { get; private set; }
61
62                 CodeDomProvider code_provider = CodeDomProvider.CreateProvider ("csharp");
63                 Dictionary<CodeNamespace,CodeIdentifiers> identifiers_table = new Dictionary<CodeNamespace,CodeIdentifiers> ();
64                 ImportOptions import_options;
65
66                 public ImportOptions Options {
67                         get { return import_options; }
68                         set {
69                                 import_options = value;
70                                 code_provider = value.CodeProvider ?? code_provider;
71                         }
72                 }
73
74                 // CanImport
75
76                 public bool CanImport (XmlSchemaSet schemas)
77                 {
78                         if (schemas == null)
79                                 throw new ArgumentNullException ("schemas");
80
81                         if (!schemas.IsCompiled)
82                                 schemas.Compile ();
83
84                         foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
85                                 if (!CanImport (schemas, xe))
86                                         return false;
87                         return true;
88                 }
89
90                 public bool CanImport (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
91                 {
92                         if (schemas == null)
93                                 throw new ArgumentNullException ("schemas");
94                         if (typeNames == null)
95                                 throw new ArgumentNullException ("typeNames");
96
97                         if (!schemas.IsCompiled)
98                                 schemas.Compile ();
99
100                         foreach (var name in typeNames)
101                                 if (!CanImport (schemas, name))
102                                         return false;
103                         return true;
104                 }
105
106                 public bool CanImport (XmlSchemaSet schemas, XmlQualifiedName typeName)
107                 {
108                         if (schemas == null)
109                                 throw new ArgumentNullException ("schemas");
110                         if (typeName == null)
111                                 throw new ArgumentNullException ("typeName");
112
113                         if (!schemas.IsCompiled)
114                                 schemas.Compile ();
115
116                         if (!schemas.GlobalTypes.Contains (typeName))
117                                 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
118
119                         return CanImport (schemas, schemas.GlobalTypes [typeName] as XmlSchemaComplexType);
120                 }
121
122                 public bool CanImport (XmlSchemaSet schemas, XmlSchemaElement element)
123                 {
124                         if (schemas == null)
125                                 throw new ArgumentNullException ("schemas");
126
127                         if (!schemas.IsCompiled)
128                                 schemas.Compile ();
129
130                         return CanImport (schemas, element.ElementSchemaType as XmlSchemaComplexType);
131                 }
132
133                 bool CanImport (XmlSchemaSet schemas, XmlSchemaComplexType type)
134                 {
135                         if (type == null || type.QualifiedName.Namespace == XmlSchema.Namespace) // xs:anyType -> not supported.
136                                 return false;
137
138                         if (type.ContentModel is XmlSchemaSimpleContent) // simple content derivation is not supported.
139                                 return false;
140                         if (type.ContentModel != null && type.ContentModel.Content != null) {
141                                 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
142                                 if (xscce == null) // complex DBR is not supported.
143                                         return false;
144                                 // check base type
145                                 if (xscce.BaseTypeName != qname_anytype && !CanImport (schemas, xscce.BaseTypeName))
146                                         return false;
147                         }
148
149                         return true;
150                 }
151
152                 // Import
153
154                 public void Import (XmlSchemaSet schemas)
155                 {
156                         if (schemas == null)
157                                 throw new ArgumentNullException ("schemas");
158
159                         if (!schemas.IsCompiled)
160                                 schemas.Compile ();
161
162                         foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
163                                 Import (schemas, xe);
164                 }
165
166                 public void Import (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
167                 {
168                         if (schemas == null)
169                                 throw new ArgumentNullException ("schemas");
170                         if (typeNames == null)
171                                 throw new ArgumentNullException ("typeNames");
172                         foreach (var name in typeNames)
173                                 Import (schemas, name);
174                 }
175
176                 // This checks type existence and raises an error if it is missing.
177                 public void Import (XmlSchemaSet schemas, XmlQualifiedName typeName)
178                 {
179                         if (schemas == null)
180                                 throw new ArgumentNullException ("schemas");
181                         if (typeName == null)
182                                 throw new ArgumentNullException ("typeName");
183
184                         if (IsPredefinedType (typeName))
185                                 return;
186
187                         if (!schemas.GlobalTypes.Contains (typeName))
188                                 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
189
190                         Import (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType, typeName);
191                 }
192
193                 public XmlQualifiedName Import (XmlSchemaSet schemas, XmlSchemaElement element)
194                 {
195                         if (schemas == null)
196                                 throw new ArgumentNullException ("schemas");
197                         if (element == null)
198                                 throw new ArgumentNullException ("element");
199
200                         var elname = element.QualifiedName;
201
202                         switch (elname.Namespace) {
203                         case KnownTypeCollection.MSSimpleNamespace:
204                                 switch (elname.Name) {
205                                 case "char":
206                                 case "duration":
207                                 case "guid":
208                                         return elname;
209                                 }
210                                 break;
211                         }
212
213                         // FIXME: use element to fill nillable and arrays.
214                         var qname = element.SchemaType != null ? element.QualifiedName : element.ElementSchemaType.QualifiedName;
215                         Import (schemas, element.ElementSchemaType, qname);
216                         return qname;
217                 }
218
219                 void Import (XmlSchemaSet schemas, XmlSchemaType type)
220                 {
221                         Import (schemas, type, type.QualifiedName);
222                 }
223
224                 void Import (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
225                 {
226                         var existing = imported_types.FirstOrDefault (it => it.XsdType == type);
227                         if (existing != null)
228                                 return;// existing.XsdTypeName;
229
230                         if (IsPredefinedType (type.QualifiedName))
231                                 return;
232
233                         DoImport (schemas, type, qname);
234                 }
235
236                 string GetUniqueName (string name, CodeNamespace cns)
237                 {
238                         CodeIdentifiers i;
239                         if (!identifiers_table.TryGetValue (cns, out i)) {
240                                 i = new CodeIdentifiers ();
241                                 identifiers_table.Add (cns, i);
242                         }
243                         return i.AddUnique (name, null);
244                 }
245
246                 void DoImport (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
247                 {
248                         CodeNamespace cns = null;
249                         CodeTypeReference clrRef;
250                         cns = GetCodeNamespace (qname);
251                         clrRef = new CodeTypeReference (cns.Name.Length > 0 ? cns.Name + "." + qname.Name : qname.Name);
252
253                         var td = new CodeTypeDeclaration () {
254                                 Name = GetUniqueName (CodeIdentifier.MakeValid (qname.Name), cns),
255                                 TypeAttributes = GenerateInternal ? TypeAttributes.NotPublic : TypeAttributes.Public };
256                         cns.Types.Add (td);
257
258                         var info = new TypeImportInfo () { ClrType = clrRef, XsdType = type,  XsdTypeName = qname };
259                         imported_types.Add (info);
260
261                         var st = type as XmlSchemaSimpleType;
262                         if (st != null) {
263                                 ImportSimpleType (td, schemas, st, qname);
264                         } else {
265                                 var ct = (XmlSchemaComplexType) type;
266                                 var sc = ct.ContentModel as XmlSchemaSimpleContent;
267                                 if (sc != null) {
268                                         if (sc.Content is XmlSchemaSimpleContentExtension)
269                                                 throw new InvalidDataContractException (String.Format ("complex type '{0}' with simple content extension is not supported", type.QualifiedName));
270                                 }
271                                 if (!ImportComplexType (td, schemas, ct, qname)) {
272                                         cns.Types.Remove (td);
273                                         if (cns.Types.Count == 0)
274                                                 CodeCompileUnit.Namespaces.Remove (cns);
275                                 }
276                         }
277
278                         foreach (var impinfo in imported_types)
279                                 for (; impinfo.KnownTypeOutputIndex < impinfo.KnownClrTypes.Count; impinfo.KnownTypeOutputIndex++)
280                                         td.CustomAttributes.Add (new CodeAttributeDeclaration (
281                                                 new CodeTypeReference (typeof (KnownTypeAttribute)),
282                                                 new CodeAttributeArgument (new CodeTypeOfExpression (impinfo.KnownClrTypes [impinfo.KnownTypeOutputIndex]))));
283                 }
284
285                 static readonly string ass_name = typeof (DataContractAttribute).Assembly.GetName ().Name;
286                 static readonly string ass_version = typeof (DataContractAttribute).Assembly.GetName ().Version.ToString ();
287                 static readonly CodeTypeReference typeref_data_contract = new CodeTypeReference (typeof (DataContractAttribute));
288                 static readonly CodeTypeReference typeref_coll_contract = new CodeTypeReference (typeof (CollectionDataContractAttribute));
289
290                 void AddTypeAttributes (CodeTypeDeclaration td, XmlSchemaType type, params XmlSchemaElement [] collectionArgs)
291                 {
292                         var name = type.QualifiedName;
293                         // [GeneratedCode (assembly_name, assembly_version)]
294                         td.CustomAttributes.Add (new CodeAttributeDeclaration (
295                                 new CodeTypeReference (typeof (GeneratedCodeAttribute)),
296                                 new CodeAttributeArgument (new CodePrimitiveExpression (ass_name)),
297                                 new CodeAttributeArgument (new CodePrimitiveExpression (ass_version))));
298
299                         var ct = type as XmlSchemaComplexType;
300
301                         // [DataContract(Name="foobar",Namespace="urn:foobar")] (optionally IsReference=true),
302                         // or [CollectionDataContract(ditto, ItemType/KeyType/ValueType)]
303                         var dca = new CodeAttributeDeclaration (
304                                 collectionArgs != null && collectionArgs.Length > 0 ? typeref_coll_contract : typeref_data_contract,
305                                 new CodeAttributeArgument ("Name", new CodePrimitiveExpression (name.Name)),
306                                 new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (name.Namespace)));
307                         if (collectionArgs != null) {
308                                 if (collectionArgs.Length > 0)
309                                         dca.Arguments.Add (new CodeAttributeArgument ("ItemName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [0].QualifiedName.Name))));
310                                 if (collectionArgs.Length > 2) {
311                                         dca.Arguments.Add (new CodeAttributeArgument ("KeyName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [1].QualifiedName.Name))));
312                                         dca.Arguments.Add (new CodeAttributeArgument ("ValueName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [2].QualifiedName.Name))));
313                                 }
314                         }
315                         if (ct != null && ct.AttributeUses [new XmlQualifiedName ("Ref", KnownTypeCollection.MSSimpleNamespace)] != null)
316                                 dca.Arguments.Add (new CodeAttributeArgument ("IsReference", new CodePrimitiveExpression (true)));
317                         td.CustomAttributes.Add (dca);
318
319                         // optional [Serializable]
320                         if (Options != null && Options.GenerateSerializable)
321                                 td.CustomAttributes.Add (new CodeAttributeDeclaration ("System.SerializableAttribute"));
322                 }
323
324                 static readonly CodeTypeReference typeref_ext_iface = new CodeTypeReference ("System.Runtime.Serialization.IExtensibleDataObject");
325                 static readonly CodeTypeReference typeref_ext_class = new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject");
326
327                 void AddExtensionData (CodeTypeDeclaration td)
328                 {
329                         td.BaseTypes.Add (typeref_ext_iface);
330
331                         var field = new CodeMemberField (typeref_ext_class, "extensionDataField");
332                         td.Members.Add (field);
333
334                         var prop = new CodeMemberProperty () { Type = field.Type, Name = "ExtensionData", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final };
335                         prop.GetStatements.Add (new CodeMethodReturnStatement (
336                                 new CodeFieldReferenceExpression (
337                                 new CodeThisReferenceExpression (),
338                                 "extensionDataField")));
339                         prop.SetStatements.Add (new CodeAssignStatement (
340                                 new CodeFieldReferenceExpression (
341                                 new CodeThisReferenceExpression (),
342                                 "extensionDataField"),
343                                 new CodePropertySetValueReferenceExpression ()));
344
345                         td.Members.Add (prop);
346                 }
347
348                 void ImportSimpleType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleType type, XmlQualifiedName qname)
349                 {
350                         var scl = type.Content as XmlSchemaSimpleTypeList;
351                         if (scl != null) {
352                                 if (scl.ItemType == null)
353                                         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));
354                                 var itemType = scl.ItemType as XmlSchemaSimpleType;
355                                 var ir = itemType.Content as XmlSchemaSimpleTypeRestriction;
356                                 if (ir == null)
357                                         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));
358                                 ImportEnum (td, schemas, ir, type, qname, true);
359                                 return;
360                         }
361                         var scr = type.Content as XmlSchemaSimpleTypeRestriction;
362                         if (scr != null) {
363                                 ImportEnum (td, schemas, scr, type, qname, false);
364                                 return;
365                         }
366
367                         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));
368                 }
369
370                 static readonly CodeTypeReference enum_member_att_ref = new CodeTypeReference (typeof (EnumMemberAttribute));
371
372                 void ImportEnum (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleTypeRestriction r, XmlSchemaType type, XmlQualifiedName qname, bool isFlag)
373                 {
374                         if (isFlag && !r.BaseTypeName.Equals (new XmlQualifiedName ("string", XmlSchema.Namespace)))
375                                 throw new InvalidDataContractException (String.Format ("For flags enumeration '{0}', the base type for the simple type restriction must be XML schema string", qname));
376
377                         td.IsEnum = true;
378                         AddTypeAttributes (td, type);
379                         if (isFlag)
380                                 td.CustomAttributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (typeof (FlagsAttribute))));
381
382                         foreach (var facet in r.Facets) {
383                                 var e = facet as XmlSchemaEnumerationFacet;
384                                 if (e == null)
385                                         throw new InvalidDataContractException (String.Format ("Invalid simple type restriction (type {0}). Only enumeration is allowed.", qname));
386                                 var em = new CodeMemberField () { Name = CodeIdentifier.MakeValid (e.Value) };
387                                 var ea = new CodeAttributeDeclaration (enum_member_att_ref);
388                                 if (e.Value != em.Name)
389                                         ea.Arguments.Add (new CodeAttributeArgument ("Value", new CodePrimitiveExpression (e.Value)));
390                                 em.CustomAttributes.Add (ea);
391                                 td.Members.Add (em);
392                         }
393                 }
394
395                 // Returns false if it should remove the imported type.
396                 // FIXME: also support ImportXmlType
397                 bool ImportComplexType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaComplexType type, XmlQualifiedName qname)
398                 {
399                         foreach (XmlSchemaAttribute att in type.AttributeUses.Values)
400                                 if (att.Use != XmlSchemaUse.Optional || att.QualifiedName.Namespace != KnownTypeCollection.MSSimpleNamespace)
401                                         throw new InvalidDataContractException (String.Format ("attribute in DataContract complex type '{0}' is limited to those in {1} namespace, and optional.", qname, KnownTypeCollection.MSSimpleNamespace));
402
403                         CodeTypeReference baseClrType = null;
404                         var particle = type.Particle;
405                         if (type.ContentModel != null) {
406                                 var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction;
407                                 if (xsscr != null) {
408                                         if (xsscr.BaseType != null)
409                                                 Import (schemas, xsscr.BaseType);
410                                         else
411                                                 Import (schemas, xsscr.BaseTypeName);
412                                         // The above will result in an error, but make sure to show we don't support it.
413                                         throw new InvalidDataContractException (String.Format ("complex type simple content restriction is not supported in DataContract (type '{0}')", qname));
414                                 }
415                                 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
416                                 if (xscce != null) {
417                                         Import (schemas, xscce.BaseTypeName);
418                                         baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false);
419                                         if (baseClrType != null)
420                                                 td.BaseTypes.Add (baseClrType);
421
422                                         var baseInfo = GetTypeInfo (xscce.BaseTypeName, false);
423                                         if (baseInfo != null)
424                                                 baseInfo.KnownClrTypes.Add (imported_types.First (it => it.XsdType == type).ClrType);
425                                         particle = xscce.Particle;
426                                 }
427                                 var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction;
428                                 if (xsccr != null)
429                                         throw new InvalidDataContractException (String.Format ("complex content type (for type '{0}') has a restriction content model, which is not supported in DataContract.", qname));
430                         }
431
432                         var seq = particle as XmlSchemaSequence;
433                         if (seq == null && particle != null)
434                                 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));
435
436                         if (seq != null) {
437
438                         foreach (var child in seq.Items)
439                                 if (!(child is XmlSchemaElement))
440                                         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));
441
442                         bool isDictionary = false;
443                         if (type.Annotation != null) {
444                                 foreach (var ann in type.Annotation.Items) {
445                                         var ai = ann as XmlSchemaAppInfo;
446                                         if (ai != null && ai.Markup != null &&
447                                             ai.Markup.Length > 0 &&
448                                             ai.Markup [0].NodeType == XmlNodeType.Element &&
449                                             ai.Markup [0].LocalName == "IsDictionary" &&
450                                             ai.Markup [0].NamespaceURI == KnownTypeCollection.MSSimpleNamespace)
451                                                 isDictionary = true;
452                                 }
453                         }
454
455                         if (seq.Items.Count == 1) {
456                                 var xe = (XmlSchemaElement) seq.Items [0];
457                                 if (xe.MaxOccursString == "unbounded") {
458                                         // import as a collection contract.
459                                         if (isDictionary) {
460                                                 var kvt = xe.ElementSchemaType as XmlSchemaComplexType;
461                                                 var seq2 = kvt != null ? kvt.Particle as XmlSchemaSequence : null;
462                                                 var k = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [0] as XmlSchemaElement : null;
463                                                 var v = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [1] as XmlSchemaElement : null;
464                                                 if (k == null || v == null)
465                                                         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));
466                                                 Import (schemas, k.ElementSchemaType);
467                                                 Import (schemas, v.ElementSchemaType);
468                                                 td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.Dictionary", GetCodeTypeReference (k.ElementSchemaType.QualifiedName), GetCodeTypeReference (v.ElementSchemaType.QualifiedName)));
469                                                 AddTypeAttributes (td, type, xe, k, v);
470                                                 return true;
471                                         } else if (type.QualifiedName.Namespace == KnownTypeCollection.MSArraysNamespace &&
472                                                    IsPredefinedType (xe.ElementSchemaType.QualifiedName)) {
473                                                 // then this CodeTypeDeclaration is to be removed, and CodeTypeReference to this type should be an array instead.
474                                                 var cti = imported_types.First (i => i.XsdType == type);
475                                                 cti.ClrType = new CodeTypeReference (GetCodeTypeReference (xe.ElementSchemaType.QualifiedName), 1);
476                                         
477                                                 return false;
478                                         }
479                                         else
480                                                 Import (schemas, xe.ElementSchemaType);
481                                         td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.List", GetCodeTypeReference (xe.ElementSchemaType.QualifiedName)));
482                                         AddTypeAttributes (td, type, xe);
483                                         return true;
484                                 }
485                         }
486                         if (isDictionary)
487                                 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));
488
489                         // import as a (normal) contract.
490                         var elems = new List<XmlSchemaElement> ();
491                         foreach (XmlSchemaElement xe in seq.Items) {
492                                 if (xe.MaxOccurs != 1)
493                                         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));
494
495                                 if (elems.Any (e => e.QualifiedName.Name == xe.QualifiedName.Name))
496                                         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));
497
498                                 elems.Add (xe);
499                         }
500                         foreach (var xe in elems) {
501                                 // import property type in prior.
502                                 Import (schemas, xe.ElementSchemaType.QualifiedName);
503                                 AddProperty (td, xe);
504                         }
505
506                         } // if (seq != 0)
507
508                         AddTypeAttributes (td, type);
509                         AddExtensionData (td);
510
511                         return true;
512                 }
513
514                 static readonly CodeExpression this_expr = new CodeThisReferenceExpression ();
515                 static readonly CodeExpression arg_value_expr = new CodePropertySetValueReferenceExpression ();
516
517                 bool GenerateInternal {
518                         get { return Options != null && Options.GenerateInternal; }
519                 }
520
521                 void AddProperty (CodeTypeDeclaration td, XmlSchemaElement xe)
522                 {
523                         var att = GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public;
524                         var fi = new CodeMemberField () { Name = CodeIdentifier.MakeValid (xe.QualifiedName.Name + "Field"), Type = GetCodeTypeReference (xe.ElementSchemaType.QualifiedName, xe) };
525                         td.Members.Add (fi);
526                         var pi = new CodeMemberProperty () { Name = xe.QualifiedName.Name, Attributes = att, HasGet = true, HasSet = true, Type = fi.Type };
527                         // [DataMember(Name=foobar, IsRequired=!nillable)]
528                         var dma = new CodeAttributeDeclaration (
529                                 new CodeTypeReference (typeof (DataMemberAttribute)));
530                         if (fi.Name != xe.QualifiedName.Name)
531                                 new CodeAttributeArgument ("Name", new CodePrimitiveExpression (xe.QualifiedName.Name));
532                         if (!xe.IsNillable)
533                                 new CodeAttributeArgument ("IsRequired", new CodePrimitiveExpression (true));
534                         pi.CustomAttributes.Add (dma);
535
536                         pi.GetStatements.Add (new CodeMethodReturnStatement () { Expression = new CodeFieldReferenceExpression (this_expr, fi.Name) });
537                         pi.SetStatements.Add (new CodeAssignStatement (new CodeFieldReferenceExpression (this_expr, fi.Name), arg_value_expr));
538
539
540                         td.Members.Add (pi);
541                 }
542
543                 bool IsPredefinedType (XmlQualifiedName qname)
544                 {
545                         // FIXME: support char, guid and duration (MSSimpleNamespace); fix GetPrimitiveTypeFromName() first and then this at a time.
546                         switch (qname.Namespace) {
547                         case KnownTypeCollection.MSSimpleNamespace:
548                                 switch (qname.Name) {
549                                 case "char":
550                                 case "guid":
551                                 case "duration":
552                                         return true;
553                                 }
554                                 return false;
555                         case XmlSchema.Namespace:
556                                 return KnownTypeCollection.GetPrimitiveTypeFromName (qname.Name) != null;
557                         }
558                         return false;
559                 }
560
561                 CodeNamespace GetCodeNamespace (XmlQualifiedName name)
562                 {
563                         string ns = null;
564                         if (Options == null || !Options.Namespaces.TryGetValue (name.Namespace, out ns))
565                                 ns = GetCodeNamespaceFromXmlns (name.Namespace);
566
567                         foreach (CodeNamespace cns in CodeCompileUnit.Namespaces)
568                                 if (cns.Name == ns)
569                                         return cns;
570                         var newCns = new CodeNamespace () { Name = ns };
571                         CodeCompileUnit.Namespaces.Add (newCns);
572                         return newCns;
573                 }
574
575                 const string default_ns_prefix = "http://schemas.datacontract.org/2004/07/";
576
577                 string GetCodeNamespaceFromXmlns (string xns)
578                 {
579                         if (xns.StartsWith (default_ns_prefix, StringComparison.Ordinal))
580                                 xns = xns.Substring (default_ns_prefix.Length);
581                         else {
582                                 Uri u;
583                                 string tmp;
584                                 if (Uri.TryCreate (xns, UriKind.Absolute, out u) && (tmp = MakeStringNamespaceComponentsValid (u.GetComponents (UriComponents.Host | UriComponents.Path, UriFormat.Unescaped))).Length > 0)
585                                         xns = tmp;
586                         }
587                         return MakeStringNamespaceComponentsValid (xns);
588                 }
589
590                 static readonly char [] split_tokens = new char [] {'/', '.'};
591
592                 string MakeStringNamespaceComponentsValid (string ns)
593                 {
594                         var arr = ns.Split (split_tokens, StringSplitOptions.RemoveEmptyEntries);
595                         for (int i = 0; i < arr.Length; i++)
596                                 arr [i] = CodeIdentifier.MakeValid (arr [i]);
597                         return String.Join (".", arr);
598                 }
599
600                 // Post-compilation information retrieval
601
602                 TypeImportInfo GetTypeInfo (XmlQualifiedName typeName, bool throwError)
603                 {
604                         var info = imported_types.FirstOrDefault (i => i.XsdTypeName.Equals (typeName));
605                         if (info == null) {
606                                 if (throwError)
607                                         throw new InvalidOperationException (String.Format ("schema type '{0}' has not been imported yet. Import it first.", typeName));
608                                 return null;
609                         }
610                         return info;
611                 }
612
613                 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName)
614                 {
615                         return GetCodeTypeReferenceInternal (typeName, true);
616                 }
617
618                 CodeTypeReference GetCodeTypeReferenceInternal (XmlQualifiedName typeName, bool throwError)
619                 {
620                         if (typeName == null)
621                                 throw new ArgumentNullException ("typeName");
622
623                         switch (typeName.Namespace) {
624                         case XmlSchema.Namespace:
625                                 return new CodeTypeReference (KnownTypeCollection.GetPrimitiveTypeFromName (typeName.Name));
626                         case KnownTypeCollection.MSSimpleNamespace:
627                                 switch (typeName.Name) {
628                                 case "guid":
629                                         return new CodeTypeReference (typeof (Guid));
630                                 case "duration":
631                                         return new CodeTypeReference (typeof (TimeSpan));
632                                 }
633                                 break;
634                         }
635
636                         var info = GetTypeInfo (typeName, throwError);
637                         return info != null ? info.ClrType : null;
638                 }
639
640                 [MonoTODO ("use element argument and fill Nullable etc.")]
641                 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName, XmlSchemaElement element)
642                 {
643                         if (typeName == null)
644                                 throw new ArgumentNullException ("typeName");
645                         if (element == null)
646                                 throw new ArgumentNullException ("element");
647
648                         return GetCodeTypeReference (typeName);
649                 }
650
651                 public ICollection<CodeTypeReference> GetKnownTypeReferences (XmlQualifiedName typeName)
652                 {
653                         if (typeName == null)
654                                 throw new ArgumentNullException ("typeName");
655
656                         return GetTypeInfo (typeName, true).KnownClrTypes;
657                 }
658
659                 List<TypeImportInfo> imported_types = new List<TypeImportInfo> ();
660
661                 class TypeImportInfo
662                 {
663                         public TypeImportInfo ()
664                         {
665                                 KnownClrTypes = new List<CodeTypeReference> ();
666                         }
667
668                         public CodeTypeReference ClrType { get; set; }
669                         public XmlQualifiedName XsdTypeName { get; set; }
670                         public XmlSchemaType XsdType { get; set; }
671                         public List<CodeTypeReference> KnownClrTypes { get; private set; }
672                         public int KnownTypeOutputIndex { get; set; } // updated while importing.
673                 }
674         }
675 }