flush
[mcs.git] / class / System.Runtime.Serialization / System.Runtime.Serialization / XsdDataContractImporter.cs
blob9683660f9f6aac0d5940971ca4b0adae022b5282
1 //
2 // XsdDataContractImporter.cs
3 //
4 // Author:
5 // Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2010 Novell, Inc. http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System;
30 using System.CodeDom;
31 using System.CodeDom.Compiler;
32 using System.Collections.Generic;
33 using System.IO;
34 using System.Linq;
35 using System.Reflection;
36 using System.Xml;
37 using System.Xml.Schema;
38 using System.Xml.Serialization;
40 namespace System.Runtime.Serialization
42 [MonoTODO ("Support ImportXmlType option; support arrays; CanImport is not up to date with Import")]
43 public class XsdDataContractImporter
45 static readonly XmlQualifiedName qname_anytype = new XmlQualifiedName ("anyType", XmlSchema.Namespace);
47 public XsdDataContractImporter ()
48 : this (null)
52 public XsdDataContractImporter (CodeCompileUnit codeCompileUnit)
54 // null argument is ok.
55 CodeCompileUnit = codeCompileUnit ?? new CodeCompileUnit ();
57 // Options is null by default
60 public CodeCompileUnit CodeCompileUnit { get; private set; }
62 CodeDomProvider code_provider = CodeDomProvider.CreateProvider ("csharp");
63 ImportOptions import_options;
65 public ImportOptions Options {
66 get { return import_options; }
67 set {
68 import_options = value;
69 code_provider = value.CodeProvider ?? code_provider;
73 // CanImport
75 public bool CanImport (XmlSchemaSet schemas)
77 if (schemas == null)
78 throw new ArgumentNullException ("schemas");
80 if (!schemas.IsCompiled)
81 schemas.Compile ();
83 foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
84 if (!CanImport (schemas, xe))
85 return false;
86 return true;
89 public bool CanImport (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
91 if (schemas == null)
92 throw new ArgumentNullException ("schemas");
93 if (typeNames == null)
94 throw new ArgumentNullException ("typeNames");
96 if (!schemas.IsCompiled)
97 schemas.Compile ();
99 foreach (var name in typeNames)
100 if (!CanImport (schemas, name))
101 return false;
102 return true;
105 public bool CanImport (XmlSchemaSet schemas, XmlQualifiedName typeName)
107 if (schemas == null)
108 throw new ArgumentNullException ("schemas");
109 if (typeName == null)
110 throw new ArgumentNullException ("typeName");
112 if (!schemas.IsCompiled)
113 schemas.Compile ();
115 if (!schemas.GlobalTypes.Contains (typeName))
116 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
118 return CanImport (schemas, schemas.GlobalTypes [typeName] as XmlSchemaComplexType);
121 public bool CanImport (XmlSchemaSet schemas, XmlSchemaElement element)
123 if (schemas == null)
124 throw new ArgumentNullException ("schemas");
126 if (!schemas.IsCompiled)
127 schemas.Compile ();
129 return CanImport (schemas, element.ElementSchemaType as XmlSchemaComplexType);
132 bool CanImport (XmlSchemaSet schemas, XmlSchemaComplexType type)
134 if (type == null || type.QualifiedName.Namespace == XmlSchema.Namespace) // xs:anyType -> not supported.
135 return false;
137 if (type.ContentModel is XmlSchemaSimpleContent) // simple content derivation is not supported.
138 return false;
139 if (type.ContentModel != null && type.ContentModel.Content != null) {
140 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
141 if (xscce == null) // complex DBR is not supported.
142 return false;
143 // check base type
144 if (xscce.BaseTypeName != qname_anytype && !CanImport (schemas, xscce.BaseTypeName))
145 return false;
148 return true;
151 // Import
153 public void Import (XmlSchemaSet schemas)
155 if (schemas == null)
156 throw new ArgumentNullException ("schemas");
158 if (!schemas.IsCompiled)
159 schemas.Compile ();
161 foreach (XmlSchemaElement xe in schemas.GlobalElements.Values)
162 Import (schemas, xe);
165 public void Import (XmlSchemaSet schemas, ICollection<XmlQualifiedName> typeNames)
167 if (schemas == null)
168 throw new ArgumentNullException ("schemas");
169 if (typeNames == null)
170 throw new ArgumentNullException ("typeNames");
171 foreach (var name in typeNames)
172 Import (schemas, name);
175 // This checks type existence and raises an error if it is missing.
176 public void Import (XmlSchemaSet schemas, XmlQualifiedName typeName)
178 if (schemas == null)
179 throw new ArgumentNullException ("schemas");
180 if (typeName == null)
181 throw new ArgumentNullException ("typeName");
183 if (IsPredefinedType (typeName))
184 return;
186 if (!schemas.GlobalTypes.Contains (typeName))
187 throw new InvalidDataContractException (String.Format ("Type {0} is not found in the schemas", typeName));
189 Import (schemas, schemas.GlobalTypes [typeName] as XmlSchemaType, typeName);
192 public XmlQualifiedName Import (XmlSchemaSet schemas, XmlSchemaElement element)
194 if (schemas == null)
195 throw new ArgumentNullException ("schemas");
196 if (element == null)
197 throw new ArgumentNullException ("element");
199 var elname = element.QualifiedName;
201 switch (elname.Namespace) {
202 case KnownTypeCollection.MSSimpleNamespace:
203 switch (elname.Name) {
204 case "char":
205 case "duration":
206 case "guid":
207 return elname;
209 break;
212 // FIXME: use element to fill nillable and arrays.
213 var qname = element.SchemaType != null ? element.QualifiedName : element.ElementSchemaType.QualifiedName;
214 Import (schemas, element.ElementSchemaType, qname);
215 return qname;
218 void Import (XmlSchemaSet schemas, XmlSchemaType type)
220 Import (schemas, type, type.QualifiedName);
223 void Import (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
225 var existing = imported_types.FirstOrDefault (it => it.XsdType == type);
226 if (existing != null)
227 return;// existing.XsdTypeName;
229 if (IsPredefinedType (type.QualifiedName))
230 return;
232 DoImport (schemas, type, qname);
235 void DoImport (XmlSchemaSet schemas, XmlSchemaType type, XmlQualifiedName qname)
237 CodeNamespace cns = null;
238 CodeTypeReference clrRef;
239 cns = GetCodeNamespace (qname);
240 clrRef = new CodeTypeReference (cns.Name.Length > 0 ? cns.Name + "." + qname.Name : qname.Name);
242 var td = new CodeTypeDeclaration () {
243 Name = CodeIdentifier.MakeValid (qname.Name),
244 TypeAttributes = GenerateInternal ? TypeAttributes.NotPublic : TypeAttributes.Public };
245 cns.Types.Add (td);
247 var info = new TypeImportInfo () { ClrType = clrRef, XsdType = type, XsdTypeName = qname };
248 imported_types.Add (info);
250 var st = type as XmlSchemaSimpleType;
251 if (st != null) {
252 ImportSimpleType (td, schemas, st, qname);
253 } else {
254 var ct = (XmlSchemaComplexType) type;
255 var sc = ct.ContentModel as XmlSchemaSimpleContent;
256 if (sc != null) {
257 if (sc.Content is XmlSchemaSimpleContentExtension)
258 throw new InvalidDataContractException (String.Format ("complex type '{0}' with simple content extension is not supported", type.QualifiedName));
260 if (!ImportComplexType (td, schemas, ct, qname)) {
261 cns.Types.Remove (td);
262 if (cns.Types.Count == 0)
263 CodeCompileUnit.Namespaces.Remove (cns);
267 foreach (var impinfo in imported_types)
268 for (; impinfo.KnownTypeOutputIndex < impinfo.KnownClrTypes.Count; impinfo.KnownTypeOutputIndex++)
269 td.CustomAttributes.Add (new CodeAttributeDeclaration (
270 new CodeTypeReference (typeof (KnownTypeAttribute)),
271 new CodeAttributeArgument (new CodeTypeOfExpression (impinfo.KnownClrTypes [impinfo.KnownTypeOutputIndex]))));
274 static readonly string ass_name = typeof (DataContractAttribute).Assembly.GetName ().Name;
275 static readonly string ass_version = typeof (DataContractAttribute).Assembly.GetName ().Version.ToString ();
276 static readonly CodeTypeReference typeref_data_contract = new CodeTypeReference (typeof (DataContractAttribute));
277 static readonly CodeTypeReference typeref_coll_contract = new CodeTypeReference (typeof (CollectionDataContractAttribute));
279 void AddTypeAttributes (CodeTypeDeclaration td, XmlSchemaType type, params XmlSchemaElement [] collectionArgs)
281 var name = type.QualifiedName;
282 // [GeneratedCode (assembly_name, assembly_version)]
283 td.CustomAttributes.Add (new CodeAttributeDeclaration (
284 new CodeTypeReference (typeof (GeneratedCodeAttribute)),
285 new CodeAttributeArgument (new CodePrimitiveExpression (ass_name)),
286 new CodeAttributeArgument (new CodePrimitiveExpression (ass_version))));
288 var ct = type as XmlSchemaComplexType;
290 // [DataContract(Name="foobar",Namespace="urn:foobar")] (optionally IsReference=true),
291 // or [CollectionDataContract(ditto, ItemType/KeyType/ValueType)]
292 var dca = new CodeAttributeDeclaration (
293 collectionArgs != null && collectionArgs.Length > 0 ? typeref_coll_contract : typeref_data_contract,
294 new CodeAttributeArgument ("Name", new CodePrimitiveExpression (name.Name)),
295 new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (name.Namespace)));
296 if (collectionArgs != null) {
297 if (collectionArgs.Length > 0)
298 dca.Arguments.Add (new CodeAttributeArgument ("ItemName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [0].QualifiedName.Name))));
299 if (collectionArgs.Length > 2) {
300 dca.Arguments.Add (new CodeAttributeArgument ("KeyName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [1].QualifiedName.Name))));
301 dca.Arguments.Add (new CodeAttributeArgument ("ValueName", new CodePrimitiveExpression (CodeIdentifier.MakeValid (collectionArgs [2].QualifiedName.Name))));
304 if (ct != null && ct.AttributeUses [new XmlQualifiedName ("Ref", KnownTypeCollection.MSSimpleNamespace)] != null)
305 dca.Arguments.Add (new CodeAttributeArgument ("IsReference", new CodePrimitiveExpression (true)));
306 td.CustomAttributes.Add (dca);
308 // optional [Serializable]
309 if (Options != null && Options.GenerateSerializable)
310 td.CustomAttributes.Add (new CodeAttributeDeclaration ("System.SerializableAttribute"));
313 static readonly CodeTypeReference typeref_ext_iface = new CodeTypeReference ("System.Runtime.Serialization.IExtensibleDataObject");
314 static readonly CodeTypeReference typeref_ext_class = new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject");
316 void AddExtensionData (CodeTypeDeclaration td)
318 td.BaseTypes.Add (typeref_ext_iface);
320 var field = new CodeMemberField (typeref_ext_class, "extensionDataField");
321 td.Members.Add (field);
323 var prop = new CodeMemberProperty () { Type = field.Type, Name = "ExtensionData", Attributes = (GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public) | MemberAttributes.Final };
324 prop.GetStatements.Add (new CodeMethodReturnStatement (
325 new CodeFieldReferenceExpression (
326 new CodeThisReferenceExpression (),
327 "extensionDataField")));
328 prop.SetStatements.Add (new CodeAssignStatement (
329 new CodeFieldReferenceExpression (
330 new CodeThisReferenceExpression (),
331 "extensionDataField"),
332 new CodePropertySetValueReferenceExpression ()));
334 td.Members.Add (prop);
337 void ImportSimpleType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleType type, XmlQualifiedName qname)
339 var scl = type.Content as XmlSchemaSimpleTypeList;
340 if (scl != null) {
341 if (scl.ItemType == null)
342 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));
343 var itemType = scl.ItemType as XmlSchemaSimpleType;
344 var ir = itemType.Content as XmlSchemaSimpleTypeRestriction;
345 if (ir == null)
346 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));
347 ImportEnum (td, schemas, ir, type, qname, true);
348 return;
350 var scr = type.Content as XmlSchemaSimpleTypeRestriction;
351 if (scr != null) {
352 ImportEnum (td, schemas, scr, type, qname, false);
353 return;
356 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));
359 static readonly CodeTypeReference enum_member_att_ref = new CodeTypeReference (typeof (EnumMemberAttribute));
361 void ImportEnum (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaSimpleTypeRestriction r, XmlSchemaType type, XmlQualifiedName qname, bool isFlag)
363 if (isFlag && !r.BaseTypeName.Equals (new XmlQualifiedName ("string", XmlSchema.Namespace)))
364 throw new InvalidDataContractException (String.Format ("For flags enumeration '{0}', the base type for the simple type restriction must be XML schema string", qname));
366 td.IsEnum = true;
367 AddTypeAttributes (td, type);
368 if (isFlag)
369 td.CustomAttributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (typeof (FlagsAttribute))));
371 foreach (var facet in r.Facets) {
372 var e = facet as XmlSchemaEnumerationFacet;
373 if (e == null)
374 throw new InvalidDataContractException (String.Format ("Invalid simple type restriction (type {0}). Only enumeration is allowed.", qname));
375 var em = new CodeMemberField () { Name = CodeIdentifier.MakeValid (e.Value) };
376 var ea = new CodeAttributeDeclaration (enum_member_att_ref);
377 if (e.Value != em.Name)
378 ea.Arguments.Add (new CodeAttributeArgument ("Value", new CodePrimitiveExpression (e.Value)));
379 em.CustomAttributes.Add (ea);
380 td.Members.Add (em);
384 // Returns false if it should remove the imported type.
385 // FIXME: also support ImportXmlType
386 bool ImportComplexType (CodeTypeDeclaration td, XmlSchemaSet schemas, XmlSchemaComplexType type, XmlQualifiedName qname)
388 foreach (XmlSchemaAttribute att in type.AttributeUses.Values)
389 if (att.Use != XmlSchemaUse.Optional || att.QualifiedName.Namespace != KnownTypeCollection.MSSimpleNamespace)
390 throw new InvalidDataContractException (String.Format ("attribute in DataContract complex type '{0}' is limited to those in {1} namespace, and optional.", qname, KnownTypeCollection.MSSimpleNamespace));
392 CodeTypeReference baseClrType = null;
393 var particle = type.Particle;
394 if (type.ContentModel != null) {
395 var xsscr = type.ContentModel.Content as XmlSchemaSimpleContentRestriction;
396 if (xsscr != null) {
397 if (xsscr.BaseType != null)
398 Import (schemas, xsscr.BaseType);
399 else
400 Import (schemas, xsscr.BaseTypeName);
401 // The above will result in an error, but make sure to show we don't support it.
402 throw new InvalidDataContractException (String.Format ("complex type simple content restriction is not supported in DataContract (type '{0}')", qname));
404 var xscce = type.ContentModel.Content as XmlSchemaComplexContentExtension;
405 if (xscce != null) {
406 Import (schemas, xscce.BaseTypeName);
407 baseClrType = GetCodeTypeReferenceInternal (xscce.BaseTypeName, false);
408 if (baseClrType != null)
409 td.BaseTypes.Add (baseClrType);
411 var baseInfo = GetTypeInfo (xscce.BaseTypeName, false);
412 if (baseInfo != null)
413 baseInfo.KnownClrTypes.Add (imported_types.First (it => it.XsdType == type).ClrType);
414 particle = xscce.Particle;
416 var xsccr = type.ContentModel.Content as XmlSchemaComplexContentRestriction;
417 if (xsccr != null)
418 throw new InvalidDataContractException (String.Format ("complex content type (for type '{0}') has a restriction content model, which is not supported in DataContract.", qname));
421 var seq = particle as XmlSchemaSequence;
422 if (seq == null && particle != null)
423 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));
425 if (seq != null) {
427 foreach (var child in seq.Items)
428 if (!(child is XmlSchemaElement))
429 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));
431 bool isDictionary = false;
432 if (type.Annotation != null) {
433 foreach (var ann in type.Annotation.Items) {
434 var ai = ann as XmlSchemaAppInfo;
435 if (ai != null && ai.Markup != null &&
436 ai.Markup.Length > 0 &&
437 ai.Markup [0].NodeType == XmlNodeType.Element &&
438 ai.Markup [0].LocalName == "IsDictionary" &&
439 ai.Markup [0].NamespaceURI == KnownTypeCollection.MSSimpleNamespace)
440 isDictionary = true;
444 if (seq.Items.Count == 1) {
445 var xe = (XmlSchemaElement) seq.Items [0];
446 if (xe.MaxOccursString == "unbounded") {
447 // import as a collection contract.
448 if (isDictionary) {
449 var kvt = xe.ElementSchemaType as XmlSchemaComplexType;
450 var seq2 = kvt != null ? kvt.Particle as XmlSchemaSequence : null;
451 var k = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [0] as XmlSchemaElement : null;
452 var v = seq2 != null && seq2.Items.Count == 2 ? seq2.Items [1] as XmlSchemaElement : null;
453 if (k == null || v == null)
454 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));
455 Import (schemas, k.ElementSchemaType);
456 Import (schemas, v.ElementSchemaType);
457 td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.Dictionary", GetCodeTypeReference (k.ElementSchemaType.QualifiedName), GetCodeTypeReference (v.ElementSchemaType.QualifiedName)));
458 AddTypeAttributes (td, type, xe, k, v);
459 return true;
460 } else if (type.QualifiedName.Namespace == KnownTypeCollection.MSArraysNamespace &&
461 IsPredefinedType (xe.ElementSchemaType.QualifiedName)) {
462 // then this CodeTypeDeclaration is to be removed, and CodeTypeReference to this type should be an array instead.
463 var cti = imported_types.First (i => i.XsdType == type);
464 cti.ClrType = new CodeTypeReference (GetCodeTypeReference (xe.ElementSchemaType.QualifiedName), 1);
466 return false;
468 else
469 Import (schemas, xe.ElementSchemaType);
470 td.BaseTypes.Add (new CodeTypeReference ("System.Collections.Generic.List", GetCodeTypeReference (xe.ElementSchemaType.QualifiedName)));
471 AddTypeAttributes (td, type, xe);
472 return true;
475 if (isDictionary)
476 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));
478 // import as a (normal) contract.
479 var elems = new List<XmlSchemaElement> ();
480 foreach (XmlSchemaElement xe in seq.Items) {
481 if (xe.MaxOccurs != 1)
482 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));
484 if (elems.Any (e => e.QualifiedName.Name == xe.QualifiedName.Name))
485 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));
487 elems.Add (xe);
489 foreach (var xe in elems) {
490 // import property type in prior.
491 Import (schemas, xe.ElementSchemaType.QualifiedName);
492 AddProperty (td, xe);
495 } // if (seq != 0)
497 AddTypeAttributes (td, type);
498 AddExtensionData (td);
500 return true;
503 static readonly CodeExpression this_expr = new CodeThisReferenceExpression ();
504 static readonly CodeExpression arg_value_expr = new CodePropertySetValueReferenceExpression ();
506 bool GenerateInternal {
507 get { return Options != null && Options.GenerateInternal; }
510 void AddProperty (CodeTypeDeclaration td, XmlSchemaElement xe)
512 var att = GenerateInternal ? MemberAttributes.Assembly : MemberAttributes.Public;
513 var fi = new CodeMemberField () { Name = CodeIdentifier.MakeValid (xe.QualifiedName.Name + "Field"), Type = GetCodeTypeReference (xe.ElementSchemaType.QualifiedName, xe) };
514 td.Members.Add (fi);
515 var pi = new CodeMemberProperty () { Name = xe.QualifiedName.Name, Attributes = att, HasGet = true, HasSet = true, Type = fi.Type };
516 // [DataMember(Name=foobar, IsRequired=!nillable)]
517 var dma = new CodeAttributeDeclaration (
518 new CodeTypeReference (typeof (DataMemberAttribute)));
519 if (fi.Name != xe.QualifiedName.Name)
520 new CodeAttributeArgument ("Name", new CodePrimitiveExpression (xe.QualifiedName.Name));
521 if (!xe.IsNillable)
522 new CodeAttributeArgument ("IsRequired", new CodePrimitiveExpression (true));
523 pi.CustomAttributes.Add (dma);
525 pi.GetStatements.Add (new CodeMethodReturnStatement () { Expression = new CodeFieldReferenceExpression (this_expr, fi.Name) });
526 pi.SetStatements.Add (new CodeAssignStatement (new CodeFieldReferenceExpression (this_expr, fi.Name), arg_value_expr));
529 td.Members.Add (pi);
532 bool IsPredefinedType (XmlQualifiedName qname)
534 // FIXME: support char, guid and duration (MSSimpleNamespace); fix GetPrimitiveTypeFromName() first and then this at a time.
535 switch (qname.Namespace) {
536 case KnownTypeCollection.MSSimpleNamespace:
537 switch (qname.Name) {
538 case "char":
539 case "guid":
540 case "duration":
541 return true;
543 return false;
544 case XmlSchema.Namespace:
545 return KnownTypeCollection.GetPrimitiveTypeFromName (qname.Name) != null;
547 return false;
550 CodeNamespace GetCodeNamespace (XmlQualifiedName name)
552 string ns = null;
553 if (Options == null || !Options.Namespaces.TryGetValue (name.Namespace, out ns))
554 ns = GetCodeNamespaceFromXmlns (name.Namespace);
556 foreach (CodeNamespace cns in CodeCompileUnit.Namespaces)
557 if (cns.Name == ns)
558 return cns;
559 var newCns = new CodeNamespace () { Name = ns };
560 CodeCompileUnit.Namespaces.Add (newCns);
561 return newCns;
564 const string default_ns_prefix = "http://schemas.datacontract.org/2004/07/";
566 string GetCodeNamespaceFromXmlns (string xns)
568 if (xns.StartsWith (default_ns_prefix, StringComparison.Ordinal))
569 xns = xns.Substring (default_ns_prefix.Length);
570 else {
571 Uri u;
572 string tmp;
573 if (Uri.TryCreate (xns, UriKind.Absolute, out u) && (tmp = MakeStringNamespaceComponentsValid (u.GetComponents (UriComponents.Host | UriComponents.Path, UriFormat.Unescaped))).Length > 0)
574 xns = tmp;
576 return MakeStringNamespaceComponentsValid (xns);
579 static readonly char [] split_tokens = new char [] {'/', '.'};
581 string MakeStringNamespaceComponentsValid (string ns)
583 var arr = ns.Split (split_tokens, StringSplitOptions.RemoveEmptyEntries);
584 for (int i = 0; i < arr.Length; i++)
585 arr [i] = CodeIdentifier.MakeValid (arr [i]);
586 return String.Join (".", arr);
589 // Post-compilation information retrieval
591 TypeImportInfo GetTypeInfo (XmlQualifiedName typeName, bool throwError)
593 var info = imported_types.FirstOrDefault (i => i.XsdTypeName.Equals (typeName));
594 if (info == null) {
595 if (throwError)
596 throw new InvalidOperationException (String.Format ("schema type '{0}' has not been imported yet. Import it first.", typeName));
597 return null;
599 return info;
602 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName)
604 return GetCodeTypeReferenceInternal (typeName, true);
607 CodeTypeReference GetCodeTypeReferenceInternal (XmlQualifiedName typeName, bool throwError)
609 if (typeName == null)
610 throw new ArgumentNullException ("typeName");
612 switch (typeName.Namespace) {
613 case XmlSchema.Namespace:
614 return new CodeTypeReference (KnownTypeCollection.GetPrimitiveTypeFromName (typeName.Name));
615 case KnownTypeCollection.MSSimpleNamespace:
616 switch (typeName.Name) {
617 case "guid":
618 return new CodeTypeReference (typeof (Guid));
619 case "duration":
620 return new CodeTypeReference (typeof (TimeSpan));
622 break;
625 var info = GetTypeInfo (typeName, throwError);
626 return info != null ? info.ClrType : null;
629 [MonoTODO ("use element argument and fill Nullable etc.")]
630 public CodeTypeReference GetCodeTypeReference (XmlQualifiedName typeName, XmlSchemaElement element)
632 if (typeName == null)
633 throw new ArgumentNullException ("typeName");
634 if (element == null)
635 throw new ArgumentNullException ("element");
637 return GetCodeTypeReference (typeName);
640 public ICollection<CodeTypeReference> GetKnownTypeReferences (XmlQualifiedName typeName)
642 if (typeName == null)
643 throw new ArgumentNullException ("typeName");
645 return GetTypeInfo (typeName, true).KnownClrTypes;
648 List<TypeImportInfo> imported_types = new List<TypeImportInfo> ();
650 class TypeImportInfo
652 public TypeImportInfo ()
654 KnownClrTypes = new List<CodeTypeReference> ();
657 public CodeTypeReference ClrType { get; set; }
658 public XmlQualifiedName XsdTypeName { get; set; }
659 public XmlSchemaType XsdType { get; set; }
660 public List<CodeTypeReference> KnownClrTypes { get; private set; }
661 public int KnownTypeOutputIndex { get; set; } // updated while importing.