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 };
83 td.BaseTypes.Add (new CodeTypeReference (typeof (IXmlSerializable)));
85 var thisNodes = new CodePropertyReferenceExpression (new CodeThisReferenceExpression (), "Nodes"); // property this.Nodes
86 var xmlSerializableServices = new CodeTypeReferenceExpression (typeof (XmlSerializableServices)); // static XmlSerializableServices.
87 var qnameType = new CodeTypeReference (typeof (XmlQualifiedName));
89 // XmlQualifiedName qname = new XmlQualifiedName ({qname.Name}, {qname.Namespace});
90 td.Members.Add (new CodeMemberField () { Name = "qname", Type = qnameType, InitExpression = new CodeObjectCreateExpression (qnameType, new CodePrimitiveExpression (qname.Name), new CodePrimitiveExpression (qname.Namespace)) });
92 // public XmlNode[] Nodes { get; set; }
93 td.Members.Add (new CodeMemberProperty () { Name = "Nodes", Type = new CodeTypeReference (typeof (XmlNode [])), Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final, HasGet = true, HasSet = true });
95 // public void ReadXml(XmlReader reader) {
96 var read = new CodeMemberMethod () { Name = "ReadXml", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final };
97 read.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlReader)), "reader"));
98 // this.Nodes = XmlSerializableServices.ReadXml(reader);
100 new CodeAssignStatement (thisNodes,
101 new CodeMethodInvokeExpression (
102 new CodeMethodReferenceExpression (xmlSerializableServices, "ReadXml"),
103 new CodeArgumentReferenceExpression ("reader"))));
105 td.Members.Add (read);
107 // public void WriteXml(XmlWriter writer) {
108 var write = new CodeMemberMethod () { Name = "WriteXml",Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final };
109 write.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlWriter)), "writer"));
110 // XmlSerializableServices.WriteXml(writer, this.Nodes);
111 write.Statements.Add (
112 new CodeMethodInvokeExpression (
113 new CodeMethodReferenceExpression (xmlSerializableServices, "WriteXml"),
114 new CodeArgumentReferenceExpression ("writer"),
117 td.Members.Add (write);
119 // public XmlSchema GetSchema () { return null; }
120 var getSchema = new CodeMemberMethod () { Name = "GetSchema", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final, ReturnType = new CodeTypeReference (typeof (XmlSchema)) };
121 getSchema.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (null)));
122 td.Members.Add (getSchema);
124 // public static XmlQualifiedName ExportSchema (XmlSchemaSet schemas) {
125 var export = new CodeMemberMethod () { Name = "ExportSchema", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final | MemberAttributes.Static, ReturnType = qnameType };
126 export.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (typeof (XmlSchemaSet)), "schemas"));
127 // XmlSerializableServices.AddDefaultSchema (schemas);
128 export.Statements.Add (new CodeMethodInvokeExpression (xmlSerializableServices, "AddDefaultSchema", new CodeArgumentReferenceExpression ("schemas")));
130 export.Statements.Add (new CodeMethodReturnStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "qname")));
132 td.Members.Add (export);
137 public bool CanImport (XmlSchemaSet schemas)
140 throw new ArgumentNullException ("schemas");
142 if (!schemas.IsCompiled)
145 foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
146 if (!CanImport (schemas, xe))
151 public bool CanImport (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
154 throw new ArgumentNullException ("schemas");
155 if (typeNames == null)
156 throw new ArgumentNullException ("typeNames");
158 if (!schemas.IsCompiled)
161 foreach (var name in typeNames)
162 if (!CanImport (schemas, name))
167 public bool CanImport (XmlSchemaSet schemas, XmlQualifiedName typeName)
170 throw new ArgumentNullException ("schemas");
171 if (typeName == null)
172 throw new ArgumentNullException ("typeName");
174 if (!schemas.IsCompiled)
177 if (IsPredefinedType (typeName))
178 return true; // while it just ignores...
180 if (!schemas.GlobalTypes.Contains (typeName))
181 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
183 return CanImport (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType);
186 public bool CanImport (XmlSchemaSet schemas, XmlSchemaElement element)
189 throw new ArgumentNullException ("schemas");
191 throw new ArgumentNullException ("element");
193 if (!schemas.IsCompiled)
196 return CanImport (schemas, element.ElementSchemaType as XmlSchemaType);
201 bool CanImport (XmlSchemaSet schemas, XmlSchemaType type)
203 if (IsPredefinedType (type.QualifiedName))
206 var st = type as XmlSchemaSimpleType;
208 return CanImportSimpleType (schemas, st);
210 var ct = (XmlSchemaComplexType) type;
211 var sc = ct.ContentModel as XmlSchemaSimpleContent;
213 if (sc.Content is XmlSchemaSimpleContentExtension)
216 if (!CanImportComplexType (schemas, ct))
222 bool CanImportSimpleType (XmlSchemaSet schemas, XmlSchemaSimpleType type)
224 var scl = type.Content as XmlSchemaSimpleTypeList;
226 if (scl.ItemType == null)
228 var itemType = scl.ItemType as XmlSchemaSimpleType;
229 var ir = itemType.Content as XmlSchemaSimpleTypeRestriction;
232 return true; // as enum
234 var scr = type.Content as XmlSchemaSimpleTypeRestriction;
236 return true; // as enum
241 bool CanImportComplexType (XmlSchemaSet schemas, XmlSchemaComplexType type)
243 foreach (XmlSchemaAttribute att in type.AttributeUses.Values)
244 if (att.Use != XmlSchemaUse.Optional || att.QualifiedName.Namespace != KnownTypeCollection.MSSimpleNamespace)
247 CodeTypeReference baseClrType = null;
248 var particle = type.Particle;
249 if (type.ContentModel != null) {
250 var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction;
252 if (xsscr.BaseType != null) {
253 if (!CanImport (schemas, xsscr.BaseType))
256 if (!CanImport (schemas, xsscr.BaseTypeName))
259 // The above will result in an error, but make sure to show we don't support it.
262 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
264 if (!CanImport (schemas, xscce.BaseTypeName))
266 baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false);
268 var baseInfo = GetTypeInfo (xscce.BaseTypeName, false);
269 particle = xscce.Particle;
271 var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction;
276 var seq = particle as XmlSchemaSequence;
277 if (seq == null && particle != null)
282 if (seq.Items.Count == 1 && seq.Items [0] is XmlSchemaAny && type.Parent is XmlSchemaElement) {
284 // looks like it is not rejected (which contradicts the error message on .NET). See XsdDataContractImporterTest.ImportTestX32(). Also ImporTestX13() for Parent check.
288 foreach (var child in seq.Items)
289 if (!(child is XmlSchemaElement))
292 bool isDictionary = false;
293 if (type.Annotation != null) {
294 foreach (var ann in type.Annotation.Items) {
295 var ai = ann as XmlSchemaAppInfo;
296 if (ai != null && ai.Markup != null &&
297 ai.Markup.Length > 0 &&
298 ai.Markup [0].NodeType == XmlNodeType.Element &&
299 ai.Markup [0].LocalName == "IsDictionary" &&
300 ai.Markup [0].NamespaceURI == KnownTypeCollection.MSSimpleNamespace)
305 if (seq.Items.Count == 1) {
306 var pt = (XmlSchemaParticle) seq.Items [0];
307 var xe = pt as XmlSchemaElement;
308 if (pt.MaxOccursString == "unbounded") {
309 // import as a collection contract.
310 if (pt is XmlSchemaAny) {
311 } else if (isDictionary) {
312 var kvt = xe.ElementSchemaType as XmlSchemaComplexType;
313 var seq2 = kvt != null ? kvt.Particle as XmlSchemaSequence : null;
314 var k = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [0] as XmlSchemaElement : null;
315 var v = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [1] as XmlSchemaElement : null;
316 if (k == null || v == null)
318 if (!CanImport (schemas, k.ElementSchemaType))
320 if (!CanImport (schemas, v.ElementSchemaType))
324 } else if (type.QualifiedName.Namespace == KnownTypeCollection.MSArraysNamespace &&
325 IsPredefinedType (xe.ElementSchemaType.QualifiedName)) {
326 // then this CodeTypeDeclaration is to be removed, and CodeTypeReference to this type should be an array instead.
330 if (!CanImport (schemas, xe.ElementSchemaType))
338 // import as a (normal) contract.
339 var elems = new List<XmlSchemaElement> ();
340 foreach (XmlSchemaElement xe in seq.Items) {
341 if (xe.MaxOccurs != 1)
344 if (elems.Any (e => e.QualifiedName.Name == xe.QualifiedName.Name))
349 foreach (var xe in elems) {
350 // import property type in prior.
351 if (!CanImport (schemas, xe.ElementSchemaType.QualifiedName))
355 } // if (seq contains only an xs:any)
361 bool CanImport (XmlSchemaSet schemas, XmlSchemaComplexType type)
363 if (type == null || type.QualifiedName.Namespace == XmlSchema.Namespace) // xs:anyType -> not supported.
366 if (type.ContentModel is XmlSchemaSimpleContent) // simple content derivation is not supported.
368 if (type.ContentModel != null && type.ContentModel.Content != null) {
369 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
370 if (xscce == null) // complex DBR is not supported.
373 if (xscce.BaseTypeName != qname_anytype && !CanImport (schemas, xscce.BaseTypeName))
383 public void Import (XmlSchemaSet schemas)
386 throw new ArgumentNullException ("schemas");
388 if (!schemas.IsCompiled)
391 foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
392 Import (schemas, xe);
395 public void Import (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
398 throw new ArgumentNullException ("schemas");
399 if (typeNames == null)
400 throw new ArgumentNullException ("typeNames");
401 foreach (var name in typeNames)
402 Import (schemas, name);
405 // This checks type existence and raises an error if it is missing.
406 public void Import (XmlSchemaSet schemas, XmlQualifiedName typeName)
409 throw new ArgumentNullException ("schemas");
410 if (typeName == null)
411 throw new ArgumentNullException ("typeName");
413 if (IsPredefinedType (typeName))
416 if (!schemas.GlobalTypes.Contains (typeName))
417 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
419 Import (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType, typeName);
422 public XmlQualifiedName Import (XmlSchemaSet schemas, XmlSchemaElement element)
425 throw new ArgumentNullException ("schemas");
427 throw new ArgumentNullException ("element");
429 var elname = element.QualifiedName;
431 switch (elname.Namespace) {
432 case KnownTypeCollection.MSSimpleNamespace:
433 switch (elname.Name) {
442 if (!CanImport (schemas, element) && Options != null && Options.ImportXmlType) {
443 var qn = element.QualifiedName;
444 GenerateXmlType (qn);
448 // FIXME: use element to fill nillable and arrays.
449 var qname = element.SchemaType != null ? element.QualifiedName : element.ElementSchemaType.QualifiedName;
450 Import (schemas, element.ElementSchemaType, qname);
454 void Import (XmlSchemaSet schemas, XmlSchemaType type)
456 if (!CanImport (schemas, type) && Options != null && Options.ImportXmlType) {
457 GenerateXmlType (type.QualifiedName);
460 Import (schemas, type, type.QualifiedName);
463 void Import (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
465 var existing = imported_types.FirstOrDefault (it => it.XsdType == type);
466 if (existing != null)
467 return;// existing.XsdTypeName;
469 if (IsPredefinedType (type.QualifiedName))
472 DoImport (schemas, type, qname);
475 string GetUniqueName (string name, CodeNamespace cns)
478 if (!identifiers_table.TryGetValue (cns, out i)) {
479 i = new CodeIdentifiers ();
480 identifiers_table.Add (cns, i);
482 return i.AddUnique (name, null);
485 void DoImport (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
487 CodeNamespace cns = null;
488 CodeTypeReference clrRef;
489 cns = GetCodeNamespace (qname.Namespace);
490 clrRef = new CodeTypeReference (cns.Name.Length > 0 ? cns.Name + "." + qname.Name : qname.Name);
492 var td = new CodeTypeDeclaration () {
493 Name = GetUniqueName (CodeIdentifier.MakeValid (qname.Name), cns),
494 TypeAttributes = GenerateInternal ? TypeAttributes.NotPublic : TypeAttributes.Public };
497 var info = new TypeImportInfo () { ClrType = clrRef, XsdType = type, XsdTypeName = qname };
498 imported_types.Add (info);
500 var st = type as XmlSchemaSimpleType;
502 ImportSimpleType (td, schemas, st, qname);
504 var ct = (XmlSchemaComplexType) type;
505 var sc = ct.ContentModel as XmlSchemaSimpleContent;
507 if (sc.Content is XmlSchemaSimpleContentExtension)
508 throw new InvalidDataContractException (String.Format ("complex type '{0}' with simple content extension is not supported", type.QualifiedName));
510 if (!ImportComplexType (td, schemas, ct, qname)) {
511 cns.Types.Remove (td);
512 if (cns.Types.Count == 0)
513 CodeCompileUnit.Namespaces.Remove (cns);
516 foreach (var impinfo in imported_types)
517 for (; impinfo.KnownTypeOutputIndex < impinfo.KnownClrTypes.Count; impinfo.KnownTypeOutputIndex++)
518 td.CustomAttributes.Add (new CodeAttributeDeclaration (
519 new CodeTypeReference (typeof (KnownTypeAttribute)),
520 new CodeAttributeArgument (new CodeTypeOfExpression (impinfo.KnownClrTypes [impinfo.KnownTypeOutputIndex]))));
524 static readonly string ass_name = typeof (DataContractAttribute).Assembly.GetName ().Name;
525 static readonly string ass_version = typeof (DataContractAttribute).Assembly.GetName ().Version.ToString ();
526 static readonly CodeTypeReference typeref_data_contract = new CodeTypeReference (typeof (DataContractAttribute));
527 static readonly CodeTypeReference typeref_coll_contract = new CodeTypeReference (typeof (CollectionDataContractAttribute));
529 void AddTypeAttributes (CodeTypeDeclaration td, XmlSchemaType type, params XmlSchemaElement [] collectionArgs)
531 var name = type.QualifiedName;
532 // [GeneratedCode (assembly_name, assembly_version)]
533 td.CustomAttributes.Add (new CodeAttributeDeclaration (
534 new CodeTypeReference (typeof (GeneratedCodeAttribute)),
535 new CodeAttributeArgument (new CodePrimitiveExpression (ass_name)),
536 new CodeAttributeArgument (new CodePrimitiveExpression (ass_version))));
538 var ct = type as XmlSchemaComplexType;
540 // [DataContract(Name="foobar",Namespace="urn:foobar")] (optionally IsReference=true),
541 // or [CollectionDataContract(ditto, ItemType/KeyType/ValueType)]
542 var dca = new CodeAttributeDeclaration (
543 collectionArgs != null && collectionArgs.Length > 0 ? typeref_coll_contract : typeref_data_contract,
544 new CodeAttributeArgument ("Name", new CodePrimitiveExpression (name.Name)),
545 new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (name.Namespace)));
546 if (collectionArgs != null) {
547 if (collectionArgs.Length > 0)
548 dca.Arguments.Add (new CodeAttributeArgument ("ItemName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [0].QualifiedName.Name))));
549 if (collectionArgs.Length > 2) {
550 dca.Arguments.Add (new CodeAttributeArgument ("KeyName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [1].QualifiedName.Name))));
551 dca.Arguments.Add (new CodeAttributeArgument ("ValueName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [2].QualifiedName.Name))));
554 if (ct != null && ct.AttributeUses [new XmlQualifiedName ("Ref", KnownTypeCollection.MSSimpleNamespace)] != null)
555 dca.Arguments.Add (new CodeAttributeArgument ("IsReference", new CodePrimitiveExpression (true)));
556 td.CustomAttributes.Add (dca);
558 // optional [Serializable]
559 if (Options != null && Options.GenerateSerializable)
560 td.CustomAttributes.Add (new CodeAttributeDeclaration ("System.SerializableAttribute"));
563 static readonly CodeTypeReference typeref_ext_iface = new CodeTypeReference ("System.Runtime.Serialization.IExtensibleDataObject");
564 static readonly CodeTypeReference typeref_ext_class = new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject");
566 void AddExtensionData (CodeTypeDeclaration td)
568 td.BaseTypes.Add (typeref_ext_iface);
570 var field = new CodeMemberField (typeref_ext_class, "extensionDataField");
571 td.Members.Add (field);
573 var prop = new CodeMemberProperty () { Type = field.Type, Name = "ExtensionData", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final };
574 prop.GetStatements.Add (new CodeMethodReturnStatement (
575 new CodeFieldReferenceExpression (
576 new CodeThisReferenceExpression (),
577 "extensionDataField")));
578 prop.SetStatements.Add (new CodeAssignStatement (
579 new CodeFieldReferenceExpression (
580 new CodeThisReferenceExpression (),
581 "extensionDataField"),
582 new CodePropertySetValueReferenceExpression ()));
584 td.Members.Add (prop);
587 void ImportSimpleType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleType type, XmlQualifiedName qname)
589 var scl = type.Content as XmlSchemaSimpleTypeList;
591 if (scl.ItemType == null)
592 throw new InvalidDataContractException (String.Format ("simple type list is allowed only with an anonymous simple type with enumeration restriction content as its item type definition (type is {0})", type.QualifiedName));
593 var itemType = scl.ItemType as XmlSchemaSimpleType;
594 var ir = itemType.Content as XmlSchemaSimpleTypeRestriction;
596 throw new InvalidDataContractException (String.Format ("simple type list is allowed only with an anonymous simple type with enumeration restriction content as its item type definition (type is {0})", type.QualifiedName));
597 ImportEnum (td, schemas, ir, type, qname, true);
600 var scr = type.Content as XmlSchemaSimpleTypeRestriction;
602 ImportEnum (td, schemas, scr, type, qname, false);
606 throw new InvalidDataContractException (String.Format ("simple type is supported only if it has enumeration or list of an anonymous simple type with enumeration restriction content as its item type definition (type is {0})", qname));
609 static readonly CodeTypeReference enum_member_att_ref = new CodeTypeReference (typeof (EnumMemberAttribute));
611 void ImportEnum (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleTypeRestriction r, XmlSchemaType type, XmlQualifiedName qname, bool isFlag)
613 if (isFlag && !r.BaseTypeName.Equals (new XmlQualifiedName ("string", XmlSchema.Namespace)))
614 throw new InvalidDataContractException (String.Format ("For flags enumeration '{0}', the base type for the simple type restriction must be XML schema string", qname));
617 AddTypeAttributes (td, type);
619 td.CustomAttributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (typeof (FlagsAttribute))));
621 foreach (var facet in r.Facets) {
622 var e = facet as XmlSchemaEnumerationFacet;
624 throw new InvalidDataContractException (String.Format ("Invalid simple type restriction (type {0}). Only enumeration is allowed.", qname));
625 var em = new CodeMemberField () { Name = CodeIdentifier.MakeValid (e.Value) };
626 var ea = new CodeAttributeDeclaration (enum_member_att_ref);
627 if (e.Value != em.Name)
628 ea.Arguments.Add (new CodeAttributeArgument ("Value", new CodePrimitiveExpression (e.Value)));
629 em.CustomAttributes.Add (ea);
634 // Returns false if it should remove the imported type.
635 bool ImportComplexType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaComplexType type, XmlQualifiedName qname)
637 foreach (XmlSchemaAttribute att in type.AttributeUses.Values)
638 if (att.Use != XmlSchemaUse.Optional || att.QualifiedName.Namespace != KnownTypeCollection.MSSimpleNamespace)
639 throw new InvalidDataContractException (String.Format ("attribute in DataContract complex type '{0}' is limited to those in {1} namespace, and optional.", qname, KnownTypeCollection.MSSimpleNamespace));
641 CodeTypeReference baseClrType = null;
642 var particle = type.Particle;
643 if (type.ContentModel != null) {
644 var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction;
646 if (xsscr.BaseType != null)
647 Import (schemas, xsscr.BaseType);
649 Import (schemas, xsscr.BaseTypeName);
650 // The above will result in an error, but make sure to show we don't support it.
651 throw new InvalidDataContractException (String.Format ("complex type simple content restriction is not supported in DataContract (type '{0}')", qname));
653 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
655 Import (schemas, xscce.BaseTypeName);
656 baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false);
657 if (baseClrType != null)
658 td.BaseTypes.Add (baseClrType);
660 var baseInfo = GetTypeInfo (xscce.BaseTypeName, false);
661 if (baseInfo != null)
662 baseInfo.KnownClrTypes.Add (imported_types.First (it => it.XsdType == type).ClrType);
663 particle = xscce.Particle;
665 var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction;
667 throw new InvalidDataContractException (String.Format ("complex content type (for type '{0}') has a restriction content model, which is not supported in DataContract.", qname));
670 var seq = particle as XmlSchemaSequence;
671 if (seq == null && particle != null)
672 throw new InvalidDataContractException (String.Format ("Not supported particle {1}. In DataContract, only sequence particle is allowed as the top-level content of a complex type (type '{0}')", qname, particle));
676 if (seq.Items.Count == 1 && seq.Items [0] is XmlSchemaAny && type.Parent is XmlSchemaElement) {
678 // looks like it is not rejected (which contradicts the error message on .NET). See XsdDataContractImporterTest.ImportTestX32(). Also ImporTestX13() for Parent check.
682 foreach (var child in seq.Items)
683 if (!(child is XmlSchemaElement))
684 throw new InvalidDataContractException (String.Format ("Only local element is allowed as the content of the sequence of the top-level content of a complex type '{0}'. Other particles (sequence, choice, all, any, group ref) are not supported.", qname));
686 bool isDictionary = false;
687 if (type.Annotation != null) {
688 foreach (var ann in type.Annotation.Items) {
689 var ai = ann as XmlSchemaAppInfo;
690 if (ai != null && ai.Markup != null &&
691 ai.Markup.Length > 0 &&
692 ai.Markup [0].NodeType == XmlNodeType.Element &&
693 ai.Markup [0].LocalName == "IsDictionary" &&
694 ai.Markup [0].NamespaceURI == KnownTypeCollection.MSSimpleNamespace)
699 if (seq.Items.Count == 1) {
700 var pt = (XmlSchemaParticle) seq.Items [0];
701 var xe = pt as XmlSchemaElement;
702 if (pt.MaxOccursString == "unbounded") {
703 // import as a collection contract.
704 if (pt is XmlSchemaAny) {
705 } else if (isDictionary) {
706 var kvt = xe.ElementSchemaType as XmlSchemaComplexType;
707 var seq2 = kvt != null ? kvt.Particle as XmlSchemaSequence : null;
708 var k = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [0] as XmlSchemaElement : null;
709 var v = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [1] as XmlSchemaElement : null;
710 if (k == null || v == null)
711 throw new InvalidDataContractException (String.Format ("Invalid Dictionary contract type '{0}'. A Dictionary schema type must have a sequence particle which contains exactly two schema elements for key and value.", type.QualifiedName));
712 Import (schemas, k.ElementSchemaType);
713 Import (schemas, v.ElementSchemaType);
714 td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.Dictionary", GetCodeTypeReference (k.ElementSchemaType.QualifiedName), GetCodeTypeReference (v.ElementSchemaType.QualifiedName)));
715 AddTypeAttributes (td, type, xe, k, v);
717 } else if (type.QualifiedName.Namespace == KnownTypeCollection.MSArraysNamespace &&
718 IsPredefinedType (xe.ElementSchemaType.QualifiedName)) {
719 // then this CodeTypeDeclaration is to be removed, and CodeTypeReference to this type should be an array instead.
720 var cti = imported_types.First (i => i.XsdType == type);
721 cti.ClrType = new CodeTypeReference (GetCodeTypeReference (xe.ElementSchemaType.QualifiedName), 1);
726 Import (schemas, xe.ElementSchemaType);
727 td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.List", GetCodeTypeReference (xe.ElementSchemaType.QualifiedName)));
728 AddTypeAttributes (td, type, xe);
733 throw new InvalidDataContractException (String.Format ("complex type '{0}' is an invalid Dictionary type definition. A Dictionary must have a sequence particle with exactly two child elements", qname));
735 // import as a (normal) contract.
736 var elems = new List<XmlSchemaElement> ();
737 foreach (XmlSchemaElement xe in seq.Items) {
738 if (xe.MaxOccurs != 1)
739 throw new InvalidDataContractException (String.Format ("schema complex type '{0}' has a content sequence containing an element '{1}' with 'maxOccurs' value as more than 1, which is not supported in DataContract.", qname, xe.QualifiedName));
741 if (elems.Any (e => e.QualifiedName.Name == xe.QualifiedName.Name))
742 throw new InvalidDataContractException (String.Format ("In schema type '{0}', there already is an element whose name is {1}, where duplicate of element names are not supported.", qname, xe.QualifiedName.Name));
746 foreach (var xe in elems) {
747 // import property type in prior.
748 Import (schemas, xe.ElementSchemaType.QualifiedName);
749 AddProperty (td, xe);
752 } // if (seq contains only an xs:any)
755 AddTypeAttributes (td, type);
756 AddExtensionData (td);
761 static readonly CodeExpression this_expr = new CodeThisReferenceExpression ();
762 static readonly CodeExpression arg_value_expr = new CodePropertySetValueReferenceExpression ();
764 bool GenerateInternal {
765 get { return Options != null && Options.GenerateInternal; }
768 void AddProperty (CodeTypeDeclaration td, XmlSchemaElement xe)
770 var att = GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public;
771 var fi = new CodeMemberField () { Name = CodeIdentifier.MakeValid (xe.QualifiedName.Name + "Field"), Type = GetCodeTypeReference (xe.ElementSchemaType.QualifiedName, xe) };
773 var pi = new CodeMemberProperty () { Name = xe.QualifiedName.Name, Attributes = att, HasGet = true, HasSet = true, Type = fi.Type };
774 // [DataMember(Name=foobar, IsRequired=!nillable)]
775 var dma = new CodeAttributeDeclaration (
776 new CodeTypeReference (typeof (DataMemberAttribute)));
777 if (fi.Name != xe.QualifiedName.Name)
778 new CodeAttributeArgument ("Name", new CodePrimitiveExpression (xe.QualifiedName.Name));
780 new CodeAttributeArgument ("IsRequired", new CodePrimitiveExpression (true));
781 pi.CustomAttributes.Add (dma);
783 pi.GetStatements.Add (new CodeMethodReturnStatement () { Expression = new CodeFieldReferenceExpression (this_expr, fi.Name) });
784 pi.SetStatements.Add (new CodeAssignStatement (new CodeFieldReferenceExpression (this_expr, fi.Name), arg_value_expr));
790 bool IsPredefinedType (XmlQualifiedName qname)
792 switch (qname.Namespace) {
793 case KnownTypeCollection.MSSimpleNamespace:
794 return KnownTypeCollection.GetPrimitiveTypeFromName (qname) != null;
795 case XmlSchema.Namespace:
796 return XmlSchemaType.GetBuiltInSimpleType (qname) != null || XmlSchemaType.GetBuiltInComplexType (qname) != null;
801 CodeNamespace GetCodeNamespace (string xmlns)
804 if (Options == null || !Options.Namespaces.TryGetValue (xmlns, out ns))
805 ns = GetCodeNamespaceFromXmlns (xmlns);
807 foreach (CodeNamespace cns in CodeCompileUnit.Namespaces)
810 var newCns = new CodeNamespace () { Name = ns };
811 CodeCompileUnit.Namespaces.Add (newCns);
815 const string default_ns_prefix = "http://schemas.datacontract.org/2004/07/";
817 string GetCodeNamespaceFromXmlns (string xns)
819 if (xns.StartsWith (default_ns_prefix, StringComparison.Ordinal))
820 xns = xns.Substring (default_ns_prefix.Length);
824 if (Uri.TryCreate (xns, UriKind.Absolute, out u) && (tmp = MakeStringNamespaceComponentsValid (u.GetComponents (UriComponents.Host | UriComponents.Path, UriFormat.Unescaped))).Length > 0)
827 return MakeStringNamespaceComponentsValid (xns);
830 static readonly char [] split_tokens = new char [] {'/', '.'};
832 string MakeStringNamespaceComponentsValid (string ns)
834 var arr = ns.Split (split_tokens, StringSplitOptions.RemoveEmptyEntries);
835 for (int i = 0; i < arr.Length; i++)
836 arr [i] = CodeIdentifier.MakeValid (arr [i]);
837 return String.Join (".", arr);
840 // Post-compilation information retrieval
842 TypeImportInfo GetTypeInfo (XmlQualifiedName typeName, bool throwError)
844 var info = imported_types.FirstOrDefault (i => i.XsdTypeName.Equals (typeName));
847 throw new InvalidOperationException (String.Format ("schema type '{0}' has not been imported yet. Import it first.", typeName));
853 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName)
855 return GetCodeTypeReferenceInternal (typeName, true);
858 CodeTypeReference GetCodeTypeReferenceInternal (XmlQualifiedName typeName, bool throwError)
860 if (typeName == null)
861 throw new ArgumentNullException ("typeName");
863 switch (typeName.Namespace) {
864 case XmlSchema.Namespace:
865 case KnownTypeCollection.MSSimpleNamespace:
866 var pt = KnownTypeCollection.GetPrimitiveTypeFromName (typeName);
868 throw new ArgumentException (String.Format ("Invalid type name in a predefined namespace: {0}", typeName));
869 return new CodeTypeReference (pt);
872 var info = GetTypeInfo (typeName, throwError);
873 return info != null ? info.ClrType : null;
876 [MonoTODO ("use element argument and fill Nullable etc.")]
877 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName, XmlSchemaElement element)
879 if (typeName == null)
880 throw new ArgumentNullException ("typeName");
882 throw new ArgumentNullException ("element");
884 return GetCodeTypeReference (typeName);
887 public ICollection<CodeTypeReference> GetKnownTypeReferences (XmlQualifiedName typeName)
889 if (typeName == null)
890 throw new ArgumentNullException ("typeName");
892 return GetTypeInfo (typeName, true).KnownClrTypes;
895 List<TypeImportInfo> imported_types = new List<TypeImportInfo> ();
899 public TypeImportInfo ()
901 KnownClrTypes = new List<CodeTypeReference> ();
904 public CodeTypeReference ClrType { get; set; }
905 public XmlQualifiedName XsdTypeName { get; set; }
906 public XmlSchemaType XsdType { get; set; }
907 public List<CodeTypeReference> KnownClrTypes { get; private set; }
908 public int KnownTypeOutputIndex { get; set; } // updated while importing.