XsdDataContractImporter.cs 26 KB

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