2 // XsdDataContractImporter.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2010 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using System.CodeDom.Compiler;
32 using System.Collections.Generic;
35 using System.Reflection;
37 using System.Xml.Schema;
38 using System.Xml.Serialization;
40 using QName = System.Xml.XmlQualifiedName;
42 namespace System.Runtime.Serialization
44 [MonoTODO ("support arrays")]
45 public class XsdDataContractImporter
47 static readonly XmlQualifiedName qname_anytype = new XmlQualifiedName ("anyType", XmlSchema.Namespace);
49 public XsdDataContractImporter ()
54 public XsdDataContractImporter (CodeCompileUnit codeCompileUnit)
56 // null argument is ok.
57 CodeCompileUnit = codeCompileUnit ?? new CodeCompileUnit ();
59 // Options is null by default
62 public CodeCompileUnit CodeCompileUnit { get; private set; }
64 CodeDomProvider code_provider = CodeDomProvider.CreateProvider ("csharp");
65 Dictionary<CodeNamespace,CodeIdentifiers> identifiers_table = new Dictionary<CodeNamespace,CodeIdentifiers> ();
66 ImportOptions import_options;
68 public ImportOptions Options {
69 get { return import_options; }
71 import_options = value;
72 code_provider = value.CodeProvider ?? code_provider;
76 void GenerateXmlType (XmlQualifiedName qname)
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,
84 td.BaseTypes.Add (new CodeTypeReference (typeof (IXmlSerializable)));
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));
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)) });
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 });
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"))));
106 td.Members.Add (read);
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"),
118 td.Members.Add (write);
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);
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")));
131 export.Statements.Add (new CodeMethodReturnStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "qname")));
133 td.Members.Add (export);
138 public bool CanImport (XmlSchemaSet schemas)
141 throw new ArgumentNullException ("schemas");
143 if (!schemas.IsCompiled)
146 foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
147 if (!CanImport (schemas, xe))
152 public bool CanImport (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
155 throw new ArgumentNullException ("schemas");
156 if (typeNames == null)
157 throw new ArgumentNullException ("typeNames");
159 if (!schemas.IsCompiled)
162 foreach (var name in typeNames)
163 if (!CanImport (schemas, name))
168 public bool CanImport (XmlSchemaSet schemas, XmlQualifiedName typeName)
171 throw new ArgumentNullException ("schemas");
172 if (typeName == null)
173 throw new ArgumentNullException ("typeName");
175 if (!schemas.IsCompiled)
178 if (IsPredefinedType (typeName))
179 return true; // while it just ignores...
181 if (!schemas.GlobalTypes.Contains (typeName))
184 return CanImport (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType);
187 public bool CanImport (XmlSchemaSet schemas, XmlSchemaElement element)
190 throw new ArgumentNullException ("schemas");
192 throw new ArgumentNullException ("element");
194 if (!schemas.IsCompiled)
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);
208 bool CanImport (XmlSchemaSet schemas, XmlSchemaType type)
210 if (IsPredefinedType (type.QualifiedName))
213 var st = type as XmlSchemaSimpleType;
215 return CanImportSimpleType (schemas, st);
217 var ct = (XmlSchemaComplexType) type;
218 var sc = ct.ContentModel as XmlSchemaSimpleContent;
220 if (sc.Content is XmlSchemaSimpleContentExtension)
223 if (!CanImportComplexType (schemas, ct))
229 bool CanImportSimpleType (XmlSchemaSet schemas, XmlSchemaSimpleType type)
231 var scl = type.Content as XmlSchemaSimpleTypeList;
233 if (scl.ItemType == null)
235 var itemType = scl.ItemType as XmlSchemaSimpleType;
236 var ir = itemType.Content as XmlSchemaSimpleTypeRestriction;
239 return true; // as enum
241 var scr = type.Content as XmlSchemaSimpleTypeRestriction;
243 return true; // as enum
248 bool CanImportComplexType (XmlSchemaSet schemas, XmlSchemaComplexType type)
250 foreach (XmlSchemaAttribute att in type.AttributeUses.Values)
251 if (att.Use != XmlSchemaUse.Optional || att.QualifiedName.Namespace != KnownTypeCollection.MSSimpleNamespace)
254 CodeTypeReference baseClrType = null;
255 var particle = type.Particle;
256 if (type.ContentModel != null) {
257 var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction;
259 if (xsscr.BaseType != null) {
260 if (!CanImport (schemas, xsscr.BaseType))
263 if (!CanImport (schemas, xsscr.BaseTypeName))
266 // The above will result in an error, but make sure to show we don't support it.
269 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
271 if (!CanImport (schemas, xscce.BaseTypeName))
273 baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false);
275 var baseInfo = GetTypeInfo (xscce.BaseTypeName, false);
276 particle = xscce.Particle;
278 var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction;
283 var seq = particle as XmlSchemaSequence;
284 if (seq == null && particle != null)
289 if (seq.Items.Count == 1 && seq.Items [0] is XmlSchemaAny && type.Parent is XmlSchemaElement) {
291 // looks like it is not rejected (which contradicts the error message on .NET). See XsdDataContractImporterTest.ImportTestX32(). Also ImporTestX13() for Parent check.
295 foreach (var child in seq.Items)
296 if (!(child is XmlSchemaElement))
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)
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)
325 if (!CanImport (schemas, k.ElementSchemaType))
327 if (!CanImport (schemas, v.ElementSchemaType))
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.
337 if (!CanImport (schemas, xe.ElementSchemaType))
345 // import as a (normal) contract.
346 var elems = new List<XmlSchemaElement> ();
347 foreach (XmlSchemaElement xe in seq.Items) {
348 if (xe.MaxOccurs != 1)
351 if (elems.Any (e => e.QualifiedName.Name == xe.QualifiedName.Name))
356 foreach (var xe in elems) {
357 // import property type in prior.
358 if (!CanImport (schemas, xe.ElementSchemaType.QualifiedName))
362 } // if (seq contains only an xs:any)
368 bool CanImport (XmlSchemaSet schemas, XmlSchemaComplexType type)
370 if (type == null || type.QualifiedName.Namespace == XmlSchema.Namespace) // xs:anyType -> not supported.
373 if (type.ContentModel is XmlSchemaSimpleContent) // simple content derivation is not supported.
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.
380 if (xscce.BaseTypeName != qname_anytype && !CanImport (schemas, xscce.BaseTypeName))
390 public void Import (XmlSchemaSet schemas)
393 throw new ArgumentNullException ("schemas");
395 if (!schemas.IsCompiled)
398 foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
399 Import (schemas, xe);
402 public void Import (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
405 throw new ArgumentNullException ("schemas");
406 if (typeNames == null)
407 throw new ArgumentNullException ("typeNames");
408 foreach (var name in typeNames)
409 Import (schemas, name);
412 // This checks type existence and raises an error if it is missing.
413 public void Import (XmlSchemaSet schemas, XmlQualifiedName typeName)
416 throw new ArgumentNullException ("schemas");
417 if (typeName == null)
418 throw new ArgumentNullException ("typeName");
420 if (!schemas.IsCompiled)
423 if (IsPredefinedType (typeName))
426 if (!schemas.GlobalTypes.Contains (typeName))
427 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
429 Import (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType, typeName);
432 public XmlQualifiedName Import (XmlSchemaSet schemas, XmlSchemaElement element)
435 throw new ArgumentNullException ("schemas");
437 throw new ArgumentNullException ("element");
439 var elname = element.QualifiedName;
441 if (IsPredefinedType (element.SchemaTypeName))
444 switch (elname.Namespace) {
445 case KnownTypeCollection.MSSimpleNamespace:
446 switch (elname.Name) {
455 if (!CanImport (schemas, element) && Options != null && Options.ImportXmlType) {
456 var qn = element.QualifiedName;
457 GenerateXmlType (qn);
461 // FIXME: use element to fill nillable and arrays.
463 elname != null && !elname.Equals (QName.Empty) ? elname :
464 element.ElementSchemaType != null ? element.ElementSchemaType.QualifiedName :
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.
473 Import (schemas, XmlSchemaType.GetBuiltInComplexType (qname_anytype), qname);
478 void Import (XmlSchemaSet schemas, XmlSchemaType type)
480 if (!CanImport (schemas, type) && Options != null && Options.ImportXmlType) {
481 GenerateXmlType (type.QualifiedName);
484 Import (schemas, type, type.QualifiedName);
487 void Import (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
489 var existing = imported_types.FirstOrDefault (it => it.XsdType == type);
490 if (existing != null)
491 return;// existing.XsdTypeName;
493 if (IsPredefinedType (type.QualifiedName))
496 DoImport (schemas, type, qname);
499 string GetUniqueName (string name, CodeNamespace cns)
502 if (!identifiers_table.TryGetValue (cns, out i)) {
503 i = new CodeIdentifiers ();
504 identifiers_table.Add (cns, i);
506 return i.AddUnique (name, null);
509 void DoImport (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
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);
516 var td = new CodeTypeDeclaration () {
517 Name = GetUniqueName (CodeIdentifier.MakeValid (qname.Name), cns),
518 TypeAttributes = GenerateInternal ? TypeAttributes.NotPublic : TypeAttributes.Public,
522 var info = new TypeImportInfo () { ClrType = clrRef, XsdType = type, XsdTypeName = qname };
523 imported_types.Add (info);
525 var st = type as XmlSchemaSimpleType;
527 ImportSimpleType (td, schemas, st, qname);
529 var ct = (XmlSchemaComplexType) type;
530 var sc = ct.ContentModel as XmlSchemaSimpleContent;
532 if (sc.Content is XmlSchemaSimpleContentExtension)
533 throw new InvalidDataContractException (String.Format ("complex type '{0}' with simple content extension is not supported", type.QualifiedName));
535 if (!ImportComplexType (td, schemas, ct, qname)) {
536 cns.Types.Remove (td);
537 if (cns.Types.Count == 0)
538 CodeCompileUnit.Namespaces.Remove (cns);
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]))));
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));
554 void AddTypeAttributes (CodeTypeDeclaration td, XmlSchemaType type, params XmlSchemaElement [] collectionArgs)
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))));
563 var ct = type as XmlSchemaComplexType;
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))));
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);
583 // optional [Serializable]
584 if (Options != null && Options.GenerateSerializable)
585 td.CustomAttributes.Add (new CodeAttributeDeclaration ("System.SerializableAttribute"));
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");
591 void AddExtensionData (CodeTypeDeclaration td)
593 td.BaseTypes.Add (typeref_ext_iface);
595 var field = new CodeMemberField (typeref_ext_class, "extensionDataField");
596 td.Members.Add (field);
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 ()));
609 td.Members.Add (prop);
612 void ImportSimpleType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleType type, XmlQualifiedName qname)
614 var scl = type.Content as XmlSchemaSimpleTypeList;
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;
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);
625 var scr = type.Content as XmlSchemaSimpleTypeRestriction;
627 ImportEnum (td, schemas, scr, type, qname, false);
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));
634 static readonly CodeTypeReference enum_member_att_ref = new CodeTypeReference (typeof (EnumMemberAttribute));
636 void ImportEnum (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleTypeRestriction r, XmlSchemaType type, XmlQualifiedName qname, bool isFlag)
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));
642 AddTypeAttributes (td, type);
644 td.CustomAttributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (typeof (FlagsAttribute))));
646 foreach (var facet in r.Facets) {
647 var e = facet as XmlSchemaEnumerationFacet;
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);
659 // Returns false if it should remove the imported type.
660 bool ImportComplexType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaComplexType type, XmlQualifiedName qname)
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));
666 CodeTypeReference baseClrType = null;
667 var particle = type.Particle;
668 if (type.ContentModel != null) {
669 var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction;
671 if (xsscr.BaseType != null)
672 Import (schemas, xsscr.BaseType);
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));
678 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
680 Import (schemas, xscce.BaseTypeName);
681 baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false);
682 if (baseClrType != null)
683 td.BaseTypes.Add (baseClrType);
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;
690 var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction;
692 throw new InvalidDataContractException (String.Format ("complex content type (for type '{0}') has a restriction content model, which is not supported in DataContract.", qname));
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));
701 if (seq.Items.Count == 1 && seq.Items [0] is XmlSchemaAny && type.Parent is XmlSchemaElement) {
703 // looks like it is not rejected (which contradicts the error message on .NET). See XsdDataContractImporterTest.ImportTestX32(). Also ImporTestX13() for Parent check.
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));
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)
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);
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);
751 Import (schemas, xe.ElementSchemaType);
752 td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.List", GetCodeTypeReference (xe.ElementSchemaType.QualifiedName)));
753 AddTypeAttributes (td, type, xe);
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));
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));
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));
771 foreach (var xe in elems) {
772 // import property type in prior.
773 Import (schemas, xe.ElementSchemaType.QualifiedName);
774 AddProperty (td, xe);
777 } // if (seq contains only an xs:any)
780 AddTypeAttributes (td, type);
781 AddExtensionData (td);
786 static readonly CodeExpression this_expr = new CodeThisReferenceExpression ();
787 static readonly CodeExpression arg_value_expr = new CodePropertySetValueReferenceExpression ();
789 bool GenerateInternal {
790 get { return Options != null && Options.GenerateInternal; }
793 void AddProperty (CodeTypeDeclaration td, XmlSchemaElement xe)
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) };
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));
805 new CodeAttributeArgument ("IsRequired", new CodePrimitiveExpression (true));
806 pi.CustomAttributes.Add (dma);
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));
815 bool IsPredefinedType (XmlQualifiedName qname)
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;
828 CodeNamespace GetCodeNamespace (string xmlns)
831 if (Options == null || !Options.Namespaces.TryGetValue (xmlns, out ns))
832 ns = GetCodeNamespaceFromXmlns (xmlns);
834 foreach (CodeNamespace cns in CodeCompileUnit.Namespaces)
837 var newCns = new CodeNamespace () { Name = ns };
838 CodeCompileUnit.Namespaces.Add (newCns);
842 const string default_ns_prefix = "http://schemas.datacontract.org/2004/07/";
844 string GetCodeNamespaceFromXmlns (string xns)
846 if (xns.StartsWith (default_ns_prefix, StringComparison.Ordinal))
847 xns = xns.Substring (default_ns_prefix.Length);
851 if (Uri.TryCreate (xns, UriKind.Absolute, out u) && (tmp = MakeStringNamespaceComponentsValid (u.GetComponents (UriComponents.Host | UriComponents.Path, UriFormat.Unescaped))).Length > 0)
854 return MakeStringNamespaceComponentsValid (xns);
857 static readonly char [] split_tokens = new char [] {'/', '.'};
859 string MakeStringNamespaceComponentsValid (string ns)
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);
867 // Post-compilation information retrieval
869 TypeImportInfo GetTypeInfo (XmlQualifiedName typeName, bool throwError)
871 var info = imported_types.FirstOrDefault (i => i.XsdTypeName.Equals (typeName));
874 throw new InvalidOperationException (String.Format ("schema type '{0}' has not been imported yet. Import it first.", typeName));
880 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName)
882 return GetCodeTypeReferenceInternal (typeName, true);
885 CodeTypeReference GetCodeTypeReferenceInternal (XmlQualifiedName typeName, bool throwError)
887 if (typeName == null)
888 throw new ArgumentNullException ("typeName");
890 switch (typeName.Namespace) {
891 case XmlSchema.Namespace:
892 case KnownTypeCollection.MSSimpleNamespace:
893 var pt = KnownTypeCollection.GetPrimitiveTypeFromName (typeName);
895 throw new ArgumentException (String.Format ("Invalid type name in a predefined namespace: {0}", typeName));
896 return new CodeTypeReference (pt);
899 var info = GetTypeInfo (typeName, throwError);
900 return info != null ? info.ClrType : null;
903 [MonoTODO ("use element argument and fill Nullable etc.")]
904 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName, XmlSchemaElement element)
906 if (typeName == null)
907 throw new ArgumentNullException ("typeName");
909 throw new ArgumentNullException ("element");
911 return GetCodeTypeReference (typeName);
914 public ICollection<CodeTypeReference> GetKnownTypeReferences (XmlQualifiedName typeName)
916 if (typeName == null)
917 throw new ArgumentNullException ("typeName");
919 return GetTypeInfo (typeName, true).KnownClrTypes;
922 List<TypeImportInfo> imported_types = new List<TypeImportInfo> ();
926 public TypeImportInfo ()
928 KnownClrTypes = new List<CodeTypeReference> ();
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.