Merge pull request #3381 from krytarowski/netbsd-support-20
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlSchemaExporter.cs
1 // 
2 // System.Xml.Serialization.XmlSchemaExporter 
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // Copyright (C) Tim Coleman, 2002
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Xml;
33 using System.Xml.Schema;
34 using System.Collections;
35
36 namespace System.Xml.Serialization {
37         public class XmlSchemaExporter {
38
39                 #region Fields
40
41                 XmlSchemas schemas;
42                 Hashtable exportedMaps = new Hashtable();
43                 Hashtable exportedElements = new Hashtable();
44                 bool encodedFormat = false;
45                 XmlDocument xmlDoc;
46                 
47                 #endregion
48
49                 #region Constructors
50
51                 public XmlSchemaExporter (XmlSchemas schemas)
52                 {
53                         this.schemas = schemas;
54                 }
55
56                 internal XmlSchemaExporter (XmlSchemas schemas, bool encodedFormat)
57                 {
58                         this.encodedFormat = encodedFormat;
59                         this.schemas = schemas;
60                 }
61
62                 #endregion // Constructors
63
64                 #region Methods
65
66                 [MonoTODO]
67                 public string ExportAnyType (string ns)
68                 {
69                         throw new NotImplementedException ();
70                 }
71
72                 [MonoNotSupported("")]
73                 public string ExportAnyType (XmlMembersMapping members)
74                 {
75                         throw new NotImplementedException ();
76                 }
77
78                 public void ExportMembersMapping (XmlMembersMapping xmlMembersMapping)
79                 {
80                         ExportMembersMapping (xmlMembersMapping, true);
81                 }
82
83                 public
84                 void ExportMembersMapping (XmlMembersMapping xmlMembersMapping, bool exportEnclosingType)
85                 {
86                         ClassMap cmap = (ClassMap) xmlMembersMapping.ObjectMap;
87
88                         if (xmlMembersMapping.HasWrapperElement && exportEnclosingType)
89                         {
90                                 XmlSchema schema = GetSchema (xmlMembersMapping.Namespace);
91                                 XmlSchemaComplexType stype = new XmlSchemaComplexType ();
92         
93                                 XmlSchemaSequence particle;
94                                 XmlSchemaAnyAttribute anyAttribute;
95                                 ExportMembersMapSchema (schema, cmap, null, stype.Attributes, out particle, out anyAttribute);
96                                 stype.Particle = particle;
97                                 stype.AnyAttribute = anyAttribute;
98                                 
99                                 if (encodedFormat)
100                                 {
101                                         stype.Name = xmlMembersMapping.ElementName;
102                                         schema.Items.Add (stype);
103                                 }
104                                 else
105                                 {
106                                         XmlSchemaElement selem = new XmlSchemaElement ();
107                                         selem.Name = xmlMembersMapping.ElementName;
108                                         selem.SchemaType = stype;
109                                         schema.Items.Add (selem);
110                                 }
111                         }
112                         else
113                         {
114                                 ICollection members = cmap.ElementMembers;
115                                 if (members != null)
116                                 {
117                                         foreach (XmlTypeMapMemberElement member in members)
118                                         {
119                                                 if (member is XmlTypeMapMemberAnyElement && member.TypeData.IsListType)
120                                                 {
121                                                         XmlSchema mschema = GetSchema (xmlMembersMapping.Namespace);
122                                                         XmlSchemaParticle par = GetSchemaArrayElement (mschema, member.ElementInfo);
123                                                         if (par is XmlSchemaAny)
124                                                         {
125                                                                 XmlSchemaComplexType ct = FindComplexType (mschema.Items, "any");
126                                                                 if (ct != null) continue;
127                                                                 
128                                                                 ct = new XmlSchemaComplexType ();
129                                                                 ct.Name = "any";
130                                                                 ct.IsMixed = true;
131                                                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
132                                                                 ct.Particle = seq;
133                                                                 seq.Items.Add (par);
134                                                                 mschema.Items.Add (ct);
135                                                                 continue;
136                                                         }
137                                                 }
138                                                 
139                                                 
140                                                 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo [0];
141                                                 XmlSchema schema;
142
143                                                 if (encodedFormat)
144                                                 {
145                                                         schema = GetSchema (xmlMembersMapping.Namespace);
146                                                         ImportNamespace (schema, XmlSerializer.EncodingNamespace);
147                                                 }
148                                                 else
149                                                         schema = GetSchema (einfo.Namespace);
150                                                 
151                                                 
152                                                 XmlSchemaElement exe = FindElement (schema.Items, einfo.ElementName);
153                                                 XmlSchemaElement elem;
154                                                 
155                                                 XmlSchemaObjectContainer container = null;
156                                                 // In encoded format, the schema elements are not needed
157                                                 if (!encodedFormat)
158                                                         container = new XmlSchemaObjectContainer (schema);
159
160                                                 Type memType = member.GetType();
161                                                 if (member is XmlTypeMapMemberFlatList)
162                                                         throw new InvalidOperationException ("Unwrapped arrays not supported as parameters");
163                                                 else if (memType == typeof(XmlTypeMapMemberElement))
164                                                         elem = (XmlSchemaElement) GetSchemaElement (schema,
165                                                                 einfo, member.DefaultValue, false, container);
166                                                 else
167                                                         elem = (XmlSchemaElement) GetSchemaElement (schema,
168                                                                 einfo, false, container);
169                                                 
170                                                 if (exe != null)
171                                                 {
172                                                         if (exe.SchemaTypeName.Equals (elem.SchemaTypeName))
173                                                                 schema.Items.Remove (elem);
174                                                         else
175                                                         {
176                                                                 string s = "The XML element named '" + einfo.ElementName + "' ";
177                                                                 s += "from namespace '" + schema.TargetNamespace + "' references distinct types " + elem.SchemaTypeName.Name + " and " + exe.SchemaTypeName.Name + ". ";
178                                                                 s += "Use XML attributes to specify another XML name or namespace for the element or types.";
179                                                                 throw new InvalidOperationException (s);
180                                                         }
181                                                 }
182                                         }
183                                 }
184                         }
185                         
186                         CompileSchemas ();
187                 }
188
189                 [MonoTODO]
190                 public XmlQualifiedName ExportTypeMapping (XmlMembersMapping xmlMembersMapping)
191                 {
192                         throw new NotImplementedException ();
193                 }
194
195                 public void ExportTypeMapping (XmlTypeMapping xmlTypeMapping)
196                 {
197                         if (!xmlTypeMapping.IncludeInSchema) return;
198                         if (IsElementExported (xmlTypeMapping)) return;
199                         
200                         if (encodedFormat)
201                         {
202                                 ExportClassSchema (xmlTypeMapping);
203                                 XmlSchema schema = GetSchema (xmlTypeMapping.XmlTypeNamespace);
204                                 ImportNamespace (schema, XmlSerializer.EncodingNamespace);
205                         }
206                         else
207                         {
208                                 XmlSchema schema = GetSchema (xmlTypeMapping.Namespace);
209                                 XmlTypeMapElementInfo einfo = new XmlTypeMapElementInfo (null, xmlTypeMapping.TypeData);
210                                 einfo.Namespace = xmlTypeMapping.Namespace;
211                                 einfo.ElementName = xmlTypeMapping.ElementName;
212                                 if (xmlTypeMapping.TypeData.IsComplexType)
213                                         einfo.MappedType = xmlTypeMapping;
214                                 einfo.IsNullable = xmlTypeMapping.IsNullable;
215                                 GetSchemaElement (schema, einfo, false, new XmlSchemaObjectContainer (schema));
216                                 SetElementExported (xmlTypeMapping);
217                         }
218                         
219                         CompileSchemas ();
220                 }
221
222                 void ExportXmlSerializableSchema (XmlSchema currentSchema, XmlSerializableMapping map)
223                 {
224                 if (IsMapExported (map)) return;
225                 SetMapExported (map);
226                 
227                 if (map.Schema == null) return;
228
229                         string targetNs = map.Schema.TargetNamespace;
230                 XmlSchema existingSchema = schemas [targetNs];
231                 if (existingSchema == null)
232                 {
233                                 schemas.Add (map.Schema);
234                                 ImportNamespace (currentSchema, targetNs);
235                 }
236                 else if (existingSchema != map.Schema && !CanBeDuplicated (existingSchema, map.Schema))
237                 {
238                                 throw new InvalidOperationException("The namespace '" + targetNs +"' defined by the class '" + map.TypeFullName + "' is a duplicate.");
239                 }
240                 }
241
242                 private static bool CanBeDuplicated (XmlSchema existingSchema, XmlSchema schema)
243                 {
244                         if(XmlSchemas.IsDataSet (existingSchema) && XmlSchemas.IsDataSet (schema)
245                                 && existingSchema.Id == schema.Id)
246                                 return true;
247                         return false;
248                 }
249
250                 void ExportClassSchema (XmlTypeMapping map)
251                 {
252                         if (IsMapExported (map)) return;
253                         SetMapExported (map);
254                         
255                         if (map.TypeData.Type == typeof(object))
256                         {
257                                 foreach (XmlTypeMapping dmap in map.DerivedTypes)
258                                         if (dmap.TypeData.SchemaType == SchemaTypes.Class) ExportClassSchema (dmap);
259                                 return;
260                         }
261
262                         XmlSchema schema = GetSchema (map.XmlTypeNamespace);
263                         XmlSchemaComplexType stype = new XmlSchemaComplexType ();
264                         stype.Name = map.XmlType;
265                         schema.Items.Add (stype);
266
267                         ClassMap cmap = (ClassMap)map.ObjectMap;
268
269                         if (cmap.HasSimpleContent)
270                         {
271                                 XmlSchemaSimpleContent simple = new XmlSchemaSimpleContent ();
272                                 stype.ContentModel = simple;
273                                 XmlSchemaSimpleContentExtension ext = new XmlSchemaSimpleContentExtension ();
274                                 simple.Content = ext;
275                                 XmlSchemaSequence particle;
276                                 XmlSchemaAnyAttribute anyAttribute;
277                                 ExportMembersMapSchema (schema, cmap, map.BaseMap, ext.Attributes, out particle, out anyAttribute);
278                                 ext.AnyAttribute = anyAttribute;
279                                 if (map.BaseMap == null)
280                                         ext.BaseTypeName = cmap.SimpleContentBaseType;
281                                 else {
282                                         ext.BaseTypeName = new XmlQualifiedName (map.BaseMap.XmlType, map.BaseMap.XmlTypeNamespace);
283                                         ImportNamespace (schema, map.BaseMap.XmlTypeNamespace);
284                                         ExportClassSchema (map.BaseMap);
285                                 }
286                         }
287                         else if (map.BaseMap != null && map.BaseMap.IncludeInSchema)
288                         {
289                                 XmlSchemaComplexContent cstype = new XmlSchemaComplexContent ();
290                                 XmlSchemaComplexContentExtension ext = new XmlSchemaComplexContentExtension ();
291                                 ext.BaseTypeName = new XmlQualifiedName (map.BaseMap.XmlType, map.BaseMap.XmlTypeNamespace);
292                                 cstype.Content = ext;
293                                 stype.ContentModel = cstype;
294
295                                 XmlSchemaSequence particle;
296                                 XmlSchemaAnyAttribute anyAttribute;
297                                 ExportMembersMapSchema (schema, cmap, map.BaseMap, ext.Attributes, out particle, out anyAttribute);
298                                 ext.Particle = particle;
299                                 ext.AnyAttribute = anyAttribute;
300                                 stype.IsMixed = HasMixedContent (map);
301                                 cstype.IsMixed = BaseHasMixedContent (map);
302
303                                 ImportNamespace (schema, map.BaseMap.XmlTypeNamespace);
304                                 ExportClassSchema (map.BaseMap);
305                         }
306                         else
307                         {
308                                 XmlSchemaSequence particle;
309                                 XmlSchemaAnyAttribute anyAttribute;
310                                 ExportMembersMapSchema (schema, cmap, map.BaseMap, stype.Attributes, out particle, out anyAttribute);
311                                 stype.Particle = particle;
312                                 stype.AnyAttribute = anyAttribute;
313                                 stype.IsMixed = cmap.XmlTextCollector != null;
314                         }
315                         
316                         foreach (XmlTypeMapping dmap in map.DerivedTypes)
317                                 if (dmap.TypeData.SchemaType == SchemaTypes.Class) ExportClassSchema (dmap);
318                 }
319                 
320                 bool BaseHasMixedContent (XmlTypeMapping map)
321                 {
322                         ClassMap cmap = (ClassMap)map.ObjectMap;
323                         return (cmap.XmlTextCollector != null && (map.BaseMap != null && DefinedInBaseMap (map.BaseMap, cmap.XmlTextCollector)));
324                 }
325
326                 bool HasMixedContent (XmlTypeMapping map)
327                 {
328                         ClassMap cmap = (ClassMap)map.ObjectMap;
329                         return (cmap.XmlTextCollector != null && (map.BaseMap == null || !DefinedInBaseMap (map.BaseMap, cmap.XmlTextCollector)));
330                 }
331
332                 void ExportMembersMapSchema (XmlSchema schema, ClassMap map, XmlTypeMapping baseMap, XmlSchemaObjectCollection outAttributes, out XmlSchemaSequence particle, out XmlSchemaAnyAttribute anyAttribute)
333                 {
334                         particle = null;
335                         XmlSchemaSequence seq = new XmlSchemaSequence ();
336
337                         ICollection members = map.ElementMembers;
338                         if (members != null && !map.HasSimpleContent)
339                         {
340                                 foreach (XmlTypeMapMemberElement member in members)
341                                 {
342                                         if (baseMap != null && DefinedInBaseMap (baseMap, member)) continue;
343
344                                         Type memType = member.GetType();
345                                         if (memType == typeof(XmlTypeMapMemberFlatList))
346                                         {
347                                                 XmlSchemaParticle part = GetSchemaArrayElement (schema, member.ElementInfo);
348                                                 if (part != null) seq.Items.Add (part);
349                                         }
350                                         else if (memType == typeof(XmlTypeMapMemberAnyElement))
351                                         {
352                                                 seq.Items.Add (GetSchemaArrayElement (schema, member.ElementInfo));
353                                         }
354                                         else if (memType == typeof(XmlTypeMapMemberElement))
355                                         {
356                                                 GetSchemaElement (schema, (XmlTypeMapElementInfo) member.ElementInfo [0], 
357                                                         member.DefaultValue, true, new XmlSchemaObjectContainer (seq));
358                                         }
359                                         else
360                                         {
361                                                 GetSchemaElement (schema, (XmlTypeMapElementInfo) member.ElementInfo[0], 
362                                                         true, new XmlSchemaObjectContainer (seq));
363                                         }
364                                 }
365                         }
366
367                         if (seq.Items.Count > 0)
368                                 particle = seq;
369
370                         // Write attributes
371
372                         ICollection attributes = map.AttributeMembers;
373                         if (attributes != null)
374                         {
375                                 foreach (XmlTypeMapMemberAttribute attr in attributes) {
376                                         if (baseMap != null && DefinedInBaseMap (baseMap, attr)) continue;
377                                         outAttributes.Add (GetSchemaAttribute (schema, attr, true));
378                                 }
379                         }
380
381                         XmlTypeMapMember anyAttrMember = map.DefaultAnyAttributeMember;
382                         if (anyAttrMember != null)
383                                 anyAttribute = new XmlSchemaAnyAttribute ();
384                         else
385                                 anyAttribute = null;
386                 }
387                 
388                 XmlSchemaElement FindElement (XmlSchemaObjectCollection col, string name)
389                 {
390                         foreach (XmlSchemaObject ob in col)
391                         {
392                                 XmlSchemaElement elem = ob as XmlSchemaElement;
393                                 if (elem != null && elem.Name == name) return elem;
394                         }
395                         return null;
396                 }
397
398                 XmlSchemaComplexType FindComplexType (XmlSchemaObjectCollection col, string name)
399                 {
400                         foreach (XmlSchemaObject ob in col)
401                         {
402                                 XmlSchemaComplexType ctype = ob as XmlSchemaComplexType;
403                                 if (ctype != null && ctype.Name == name) return ctype;
404                         }
405                         return null;
406                 }
407
408                 XmlSchemaAttribute GetSchemaAttribute (XmlSchema currentSchema, XmlTypeMapMemberAttribute attinfo, bool isTypeMember)
409                 {
410                         XmlSchemaAttribute sat = new XmlSchemaAttribute ();
411                         if (attinfo.DefaultValue != System.DBNull.Value) {
412                                 sat.DefaultValue = ExportDefaultValue (attinfo.TypeData,
413                                         attinfo.MappedType, attinfo.DefaultValue);
414                         } else {
415                                 if (!attinfo.IsOptionalValueType && attinfo.TypeData.IsValueType)
416                                         sat.Use = XmlSchemaUse.Required;
417                         }
418
419                         ImportNamespace (currentSchema, attinfo.Namespace);
420
421                         XmlSchema memberSchema;
422                         if (attinfo.Namespace.Length == 0 && attinfo.Form != XmlSchemaForm.Qualified)
423                                 memberSchema = currentSchema;
424                         else
425                                 memberSchema = GetSchema (attinfo.Namespace);
426
427                         if (currentSchema == memberSchema || encodedFormat)
428                         {
429                                 sat.Name = attinfo.AttributeName;
430                                 if (isTypeMember) sat.Form = attinfo.Form;
431                                 if (attinfo.TypeData.SchemaType == SchemaTypes.Enum)
432                                 {
433                                         ImportNamespace (currentSchema, attinfo.DataTypeNamespace);
434                                         ExportEnumSchema (attinfo.MappedType);
435                                         sat.SchemaTypeName = new XmlQualifiedName (attinfo.TypeData.XmlType, attinfo.DataTypeNamespace);
436                                 }
437                                 else if (attinfo.TypeData.SchemaType == SchemaTypes.Array && TypeTranslator.IsPrimitive (attinfo.TypeData.ListItemType))
438                                 {
439                                         sat.SchemaType = GetSchemaSimpleListType (attinfo.TypeData);
440                                 }
441                                 else
442                                         sat.SchemaTypeName = new XmlQualifiedName (attinfo.TypeData.XmlType, attinfo.DataTypeNamespace);;
443                         }
444                         else
445                         {
446                                 sat.RefName = new XmlQualifiedName (attinfo.AttributeName, attinfo.Namespace);
447                                 foreach (XmlSchemaObject ob in memberSchema.Items)
448                                         if (ob is XmlSchemaAttribute && ((XmlSchemaAttribute)ob).Name == attinfo.AttributeName)
449                                                 return sat;
450                                                 
451                                 memberSchema.Items.Add (GetSchemaAttribute (memberSchema, attinfo, false));
452                         }
453                         return sat;
454                 }
455
456                 XmlSchemaParticle GetSchemaElement (XmlSchema currentSchema, XmlTypeMapElementInfo einfo, bool isTypeMember)
457                 {
458                         return GetSchemaElement (currentSchema, einfo, System.DBNull.Value, 
459                                 isTypeMember, (XmlSchemaObjectContainer) null);
460                 }
461                 
462                 XmlSchemaParticle GetSchemaElement (XmlSchema currentSchema, XmlTypeMapElementInfo einfo, bool isTypeMember, XmlSchemaObjectContainer container)
463                 {
464                         return GetSchemaElement (currentSchema, einfo, System.DBNull.Value, isTypeMember, container);
465                 }
466
467                 XmlSchemaParticle GetSchemaElement (XmlSchema currentSchema, XmlTypeMapElementInfo einfo, object defaultValue, bool isTypeMember, XmlSchemaObjectContainer container)
468                 {
469                         if (einfo.IsTextElement) return null;
470
471                         if (einfo.IsUnnamedAnyElement)
472                         {
473                                 XmlSchemaAny any = new XmlSchemaAny ();
474                                 any.MinOccurs = 0;
475                                 any.MaxOccurs = 1;
476                                 if (container != null)
477                                         container.Items.Add (any);
478                                 return any;
479                         }
480                         
481                         XmlSchemaElement selem = new XmlSchemaElement ();
482                         selem.IsNillable = einfo.IsNullable;
483                         if (container != null)
484                                 container.Items.Add (selem);
485
486                         if (isTypeMember)
487                         {
488                                 selem.MaxOccurs = 1;
489                                 selem.MinOccurs = einfo.IsNullable ? 1 : 0;
490
491                                 if ((defaultValue == DBNull.Value && einfo.TypeData.IsValueType && einfo.Member != null && !einfo.Member.IsOptionalValueType) || encodedFormat)
492                                         selem.MinOccurs = 1;
493                         }
494
495                         XmlSchema memberSchema = null;
496                         
497                         if (!encodedFormat)
498                         {
499                                 memberSchema = GetSchema (einfo.Namespace);
500                                 ImportNamespace (currentSchema, einfo.Namespace);
501                         }
502                         
503                         if (currentSchema == memberSchema || encodedFormat || !isTypeMember)
504                         {
505                                 if (isTypeMember) selem.IsNillable = einfo.IsNullable;
506                                 selem.Name = einfo.ElementName;
507
508                                 if (defaultValue != System.DBNull.Value)
509                                         selem.DefaultValue = ExportDefaultValue (einfo.TypeData,
510                                                 einfo.MappedType, defaultValue);
511
512                                 if (einfo.Form != XmlSchemaForm.Qualified)
513                                         selem.Form = einfo.Form;
514
515                                 switch (einfo.TypeData.SchemaType)
516                                 {
517                                         case SchemaTypes.XmlNode: 
518                                                 selem.SchemaType = GetSchemaXmlNodeType ();
519                                                 break;
520
521                                         case SchemaTypes.XmlSerializable:
522                                                 SetSchemaXmlSerializableType (einfo.MappedType as XmlSerializableMapping, selem);
523                                                 ExportXmlSerializableSchema (currentSchema, einfo.MappedType as XmlSerializableMapping);
524                                                 break;
525
526                                         case SchemaTypes.Enum:
527                                                 selem.SchemaTypeName = new XmlQualifiedName (einfo.MappedType.XmlType, einfo.MappedType.XmlTypeNamespace);
528                                                 ImportNamespace (currentSchema, einfo.MappedType.XmlTypeNamespace);
529                                                 ExportEnumSchema (einfo.MappedType);
530                                                 break;
531
532                                         case SchemaTypes.Array: 
533                                                 XmlQualifiedName atypeName = ExportArraySchema (einfo.MappedType, currentSchema.TargetNamespace); 
534                                                 selem.SchemaTypeName = atypeName;
535                                                 ImportNamespace (currentSchema, atypeName.Namespace);
536                                                 break;
537
538                                         case SchemaTypes.Class:
539                                                 if (einfo.MappedType.TypeData.Type != typeof(object)) {
540                                                         selem.SchemaTypeName = new XmlQualifiedName (einfo.MappedType.XmlType, einfo.MappedType.XmlTypeNamespace);
541                                                         ImportNamespace (currentSchema, einfo.MappedType.XmlTypeNamespace);
542                                                 }
543                                                 else if (encodedFormat)
544                                                         selem.SchemaTypeName = new XmlQualifiedName (einfo.MappedType.XmlType, einfo.MappedType.XmlTypeNamespace);
545                                                         
546                                                 ExportClassSchema (einfo.MappedType);
547                                                 break;
548
549                                         case SchemaTypes.Primitive:
550                                                 selem.SchemaTypeName = new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);
551                                                 if (!einfo.TypeData.IsXsdType) {
552                                                         ImportNamespace (currentSchema, einfo.MappedType.XmlTypeNamespace);
553                                                         ExportDerivedSchema (einfo.MappedType);
554                                                 }
555                                                 break;
556                                 }
557                         }
558                         else
559                         {
560                                 selem.RefName = new XmlQualifiedName (einfo.ElementName, einfo.Namespace);
561                                 foreach (XmlSchemaObject ob in memberSchema.Items)
562                                         if (ob is XmlSchemaElement && ((XmlSchemaElement)ob).Name == einfo.ElementName)
563                                                 return selem;
564
565                                 GetSchemaElement (memberSchema, einfo, defaultValue, false,
566                                         new XmlSchemaObjectContainer (memberSchema));
567                         }
568                         return selem;
569                 }
570
571                 void ImportNamespace (XmlSchema schema, string ns)
572                 {
573                         if (ns == null || ns.Length == 0 ||
574                                 ns == schema.TargetNamespace || ns == XmlSchema.Namespace) return;
575
576                         foreach (XmlSchemaObject sob in schema.Includes)
577                                 if ((sob is XmlSchemaImport) && ((XmlSchemaImport)sob).Namespace == ns) return;
578
579                         XmlSchemaImport imp = new XmlSchemaImport ();
580                         imp.Namespace = ns;
581                         schema.Includes.Add (imp);
582                 }
583
584                 bool DefinedInBaseMap (XmlTypeMapping map, XmlTypeMapMember member)
585                 {
586                         if (((ClassMap)map.ObjectMap).FindMember (member.Name) != null)
587                                 return true;
588                         else if (map.BaseMap != null)
589                                 return DefinedInBaseMap (map.BaseMap, member);
590                         else
591                                 return false;
592                 }
593
594                 XmlSchemaType GetSchemaXmlNodeType ()
595                 {
596                         XmlSchemaComplexType stype = new XmlSchemaComplexType ();
597                         stype.IsMixed = true;
598                         XmlSchemaSequence seq = new XmlSchemaSequence ();
599                         seq.Items.Add (new XmlSchemaAny ());
600                         stype.Particle = seq;
601                         return stype;
602                 }
603
604                 void SetSchemaXmlSerializableType (XmlSerializableMapping map, XmlSchemaElement elem)
605                 {
606                         if (map.SchemaType != null && map.Schema != null) {
607                                 elem.SchemaType = map.SchemaType;
608                                 return;
609                         }
610
611                         if (map.SchemaType == null && map.SchemaTypeName != null) {
612                                 elem.SchemaTypeName = map.SchemaTypeName;
613                                 elem.Name = map.SchemaTypeName.Name;
614                                 return;
615                         }
616                         XmlSchemaComplexType stype = new XmlSchemaComplexType ();
617                         XmlSchemaSequence seq = new XmlSchemaSequence ();
618                         if (map.Schema == null) {
619                                 XmlSchemaElement selem = new XmlSchemaElement ();
620                                 selem.RefName = new XmlQualifiedName ("schema",XmlSchema.Namespace);
621                                 seq.Items.Add (selem);
622                                 seq.Items.Add (new XmlSchemaAny ());
623                         } else {
624                                 XmlSchemaAny any = new XmlSchemaAny ();
625                                 any.Namespace = map.Schema.TargetNamespace;
626                                 seq.Items.Add (any);
627                         }
628                         stype.Particle = seq;
629                         elem.SchemaType = stype;
630                 }
631
632                 XmlSchemaSimpleType GetSchemaSimpleListType (TypeData typeData)
633                 {
634                         XmlSchemaSimpleType stype = new XmlSchemaSimpleType ();
635                         XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList ();
636                         TypeData itemTypeData = TypeTranslator.GetTypeData (typeData.ListItemType);
637                         list.ItemTypeName = new XmlQualifiedName (itemTypeData.XmlType, XmlSchema.Namespace);
638                         stype.Content = list;
639                         return stype;
640                 }
641
642                 XmlSchemaParticle GetSchemaArrayElement (XmlSchema currentSchema, XmlTypeMapElementInfoList infos)
643                 {
644                         int numInfos = infos.Count;
645                         if (numInfos > 0 && ((XmlTypeMapElementInfo)infos[0]).IsTextElement) numInfos--;
646                         if (numInfos == 0) return null;
647
648                         if (numInfos == 1)
649                         {
650                                 XmlSchemaParticle selem = GetSchemaElement (currentSchema, (XmlTypeMapElementInfo) infos[infos.Count-1], true);
651                                 selem.MinOccursString = "0";
652                                 selem.MaxOccursString = "unbounded";
653                                 return selem;
654                         }
655                         else
656                         {
657                                 XmlSchemaChoice schoice = new XmlSchemaChoice ();
658                                 schoice.MinOccursString = "0";
659                                 schoice.MaxOccursString = "unbounded";
660                                 foreach (XmlTypeMapElementInfo einfo in infos)
661                                 {
662                                         if (einfo.IsTextElement) continue;
663                                         schoice.Items.Add (GetSchemaElement (currentSchema, einfo, true));
664                                 }
665                                 return schoice;
666                         }
667                 }
668
669                 string ExportDefaultValue (TypeData typeData, XmlTypeMapping map, object defaultValue)
670                 {
671                         if (typeData.SchemaType == SchemaTypes.Enum) {
672                                 EnumMap enumMap = (EnumMap) map.ObjectMap;
673                                 // get corresponding xml name
674                                 return enumMap.GetXmlName (map.TypeFullName, defaultValue);
675                 }
676                         return XmlCustomFormatter.ToXmlString (typeData, defaultValue);
677                 }
678
679                 void ExportDerivedSchema(XmlTypeMapping map) {
680                         if (IsMapExported (map)) return;
681                         SetMapExported (map);
682
683                         XmlSchema schema = GetSchema (map.XmlTypeNamespace);
684                         for (int i = 0; i < schema.Items.Count; i++) {
685                                 XmlSchemaSimpleType item = schema.Items [i] as XmlSchemaSimpleType;
686                                 if (item != null && item.Name == map.ElementName)
687                                         return;
688                         }
689                         XmlSchemaSimpleType stype = new XmlSchemaSimpleType ();
690                         stype.Name = map.ElementName;
691                         schema.Items.Add (stype);
692
693                         XmlSchemaSimpleTypeRestriction rest = new XmlSchemaSimpleTypeRestriction ();
694                         rest.BaseTypeName = new XmlQualifiedName (map.TypeData.MappedType.XmlType, XmlSchema.Namespace);
695                         XmlSchemaPatternFacet facet = map.TypeData.XmlSchemaPatternFacet;
696                         if (facet != null)
697                                 rest.Facets.Add(facet);
698                         stype.Content = rest;
699                 }
700
701                 void ExportEnumSchema (XmlTypeMapping map)
702                 {
703                         if (IsMapExported (map)) return;
704                         SetMapExported (map);
705
706                         XmlSchema schema = GetSchema (map.XmlTypeNamespace);
707                         XmlSchemaSimpleType stype = new XmlSchemaSimpleType ();
708                         stype.Name = map.ElementName;
709                         schema.Items.Add (stype);
710
711                         XmlSchemaSimpleTypeRestriction rest = new XmlSchemaSimpleTypeRestriction ();
712                         rest.BaseTypeName = new XmlQualifiedName ("string",XmlSchema.Namespace);
713                         EnumMap emap = (EnumMap) map.ObjectMap;
714
715                         foreach (EnumMap.EnumMapMember emem in emap.Members)
716                         {
717                                 XmlSchemaEnumerationFacet ef = new XmlSchemaEnumerationFacet ();
718                                 ef.Value = emem.XmlName;
719                                 rest.Facets.Add (ef);
720                         }
721
722                         if (emap.IsFlags) {
723                                 XmlSchemaSimpleTypeList slist = new XmlSchemaSimpleTypeList ();
724                                 XmlSchemaSimpleType restrictionType = new XmlSchemaSimpleType ();
725                                 restrictionType.Content = rest;
726                                 slist.ItemType = restrictionType;
727                                 stype.Content = slist;
728                         } else {
729                                 stype.Content = rest;
730                         }
731                 }
732
733                 XmlQualifiedName ExportArraySchema (XmlTypeMapping map, string defaultNamespace)
734                 {
735                         ListMap lmap = (ListMap) map.ObjectMap;
736
737                         if (encodedFormat)
738                         {
739                                 string name, ns, schemaNs;
740                                 lmap.GetArrayType (-1, out name, out ns);                               
741                                 if (ns == XmlSchema.Namespace) schemaNs = defaultNamespace;
742                                 else schemaNs = ns;
743
744                                 if (IsMapExported (map)) return new XmlQualifiedName (lmap.GetSchemaArrayName (), schemaNs);
745                                 SetMapExported (map);
746
747                                 XmlSchema schema = GetSchema (schemaNs);
748                                 XmlSchemaComplexType stype = new XmlSchemaComplexType ();
749                                 stype.Name = lmap.GetSchemaArrayName ();
750                                 schema.Items.Add (stype);
751                                 
752                                 XmlSchemaComplexContent content = new XmlSchemaComplexContent();
753                                 content.IsMixed = false;
754                                 stype.ContentModel = content;
755                                 
756                                 XmlSchemaComplexContentRestriction rest = new XmlSchemaComplexContentRestriction ();
757                                 content.Content = rest;
758                                 rest.BaseTypeName = new XmlQualifiedName ("Array", XmlSerializer.EncodingNamespace);
759                                 XmlSchemaAttribute at = new XmlSchemaAttribute ();
760                                 rest.Attributes.Add (at);
761                                 at.RefName = new XmlQualifiedName ("arrayType", XmlSerializer.EncodingNamespace);
762                                 
763                                 XmlAttribute arrayType = Document.CreateAttribute ("arrayType", XmlSerializer.WsdlNamespace);
764                                 arrayType.Value = ns + (ns != "" ? ":" : "") + name;
765                                 at.UnhandledAttributes = new XmlAttribute [] { arrayType };
766                                 ImportNamespace (schema, XmlSerializer.WsdlNamespace);
767                         
768                                 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) lmap.ItemInfo[0];
769                                 if (einfo.MappedType != null)
770                                 {
771                                         switch (einfo.TypeData.SchemaType)
772                                         {
773                                                 case SchemaTypes.Enum:
774                                                         ExportEnumSchema (einfo.MappedType);
775                                                         break;
776                                                 case SchemaTypes.Array: 
777                                                         ExportArraySchema (einfo.MappedType, schemaNs); 
778                                                         break;
779                                                 case SchemaTypes.Class:
780                                                         ExportClassSchema (einfo.MappedType);
781                                                         break;
782                                         }
783                                 }
784                                 
785                                 return new XmlQualifiedName (lmap.GetSchemaArrayName (), schemaNs);
786                         }
787                         else
788                         {
789                                 if (IsMapExported (map)) return new XmlQualifiedName (map.XmlType, map.XmlTypeNamespace);
790                                 
791                                 SetMapExported (map);
792                                 XmlSchema schema = GetSchema (map.XmlTypeNamespace);
793                                 XmlSchemaComplexType stype = new XmlSchemaComplexType ();
794                                 stype.Name = map.ElementName;
795                                 schema.Items.Add (stype);
796
797                                 XmlSchemaParticle spart = GetSchemaArrayElement (schema, lmap.ItemInfo);
798                                 if (spart is XmlSchemaChoice)
799                                         stype.Particle = spart;
800                                 else
801                                 {
802                                         XmlSchemaSequence seq = new XmlSchemaSequence ();
803                                         seq.Items.Add (spart);
804                                         stype.Particle = seq;
805                                 }
806                                         
807                                 return new XmlQualifiedName (map.XmlType, map.XmlTypeNamespace);
808                         }
809                 }
810                 
811                 XmlDocument Document
812                 {
813                         get
814                         {
815                                 if (xmlDoc == null) xmlDoc = new XmlDocument ();
816                                 return xmlDoc;
817                         }
818                 }
819
820                 bool IsMapExported (XmlTypeMapping map)
821                 {
822                         if (exportedMaps.ContainsKey (GetMapKey(map))) return true;
823                         return false;
824                 }
825
826                 void SetMapExported (XmlTypeMapping map)
827                 {
828                         exportedMaps [GetMapKey(map)] = map;
829                 }
830
831                 bool IsElementExported (XmlTypeMapping map)
832                 {
833                         if (exportedElements.ContainsKey (GetMapKey(map))) return true;
834                         if (map.TypeData.Type == typeof(object)) return true;
835                         return false;
836                 }
837
838                 void SetElementExported (XmlTypeMapping map)
839                 {
840                         exportedElements [GetMapKey(map)] = map;
841                 }
842                 
843                 string GetMapKey (XmlTypeMapping map)
844                 {
845                         // Don't use type name for array types, since we can have different
846                         // classes that represent the same array type (for example
847                         // StringCollection and string[]).
848                         
849                         if (map.TypeData.IsListType)
850                                 return GetArrayKeyName (map.TypeData) + " " + map.XmlType + " " + map.XmlTypeNamespace;
851                         else
852                                 return map.TypeData.FullTypeName + " " + map.XmlType + " " + map.XmlTypeNamespace;
853                 }
854                 
855                 string GetArrayKeyName (TypeData td)
856                 {
857                         TypeData etd = td.ListItemTypeData;
858                         return "*arrayof*" + (etd.IsListType ? GetArrayKeyName (etd) : etd.FullTypeName);
859                 }
860                 
861                 void CompileSchemas ()
862                 {
863 //                      foreach (XmlSchema sc in schemas)
864 //                              sc.Compile (null);
865                 }
866
867                 XmlSchema GetSchema (string ns)
868                 {
869                         XmlSchema schema = schemas [ns];
870                         if (schema == null)
871                         {
872                                 schema = new XmlSchema ();
873                                 if (ns != null && ns.Length > 0)
874                                         schema.TargetNamespace = ns;
875                                 if (!encodedFormat)
876                                         schema.ElementFormDefault = XmlSchemaForm.Qualified;
877                                 schemas.Add (schema);
878                         }
879                         return schema;
880                 }
881
882                 #endregion // Methods
883
884                 private class XmlSchemaObjectContainer
885                 {
886                         private readonly XmlSchemaObject _xmlSchemaObject;
887
888                         public XmlSchemaObjectContainer (XmlSchema schema)
889                         {
890                                 _xmlSchemaObject = schema;
891                         }
892
893                         public XmlSchemaObjectContainer (XmlSchemaGroupBase group)
894                         {
895                                 _xmlSchemaObject = group;
896                         }
897
898                         public XmlSchemaObjectCollection Items {
899                                 get {
900                                         if (_xmlSchemaObject is XmlSchema) {
901                                                 return ((XmlSchema) _xmlSchemaObject).Items;
902                                         } else {
903                                                 return ((XmlSchemaGroupBase) _xmlSchemaObject).Items;
904                                         }
905                                 }
906                         }
907                 }
908         }
909 }