2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[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                 public void ExportMembersMapping (XmlMembersMapping xmlMembersMapping)
73                 {
74                         ExportMembersMapping (xmlMembersMapping, true);
75                 }
76
77 #if NET_2_0
78                 public
79 #else
80                 internal 
81 #endif
82                 void ExportMembersMapping (XmlMembersMapping xmlMembersMapping, bool exportEnclosingType)
83                 {
84                         ClassMap cmap = (ClassMap) xmlMembersMapping.ObjectMap;
85
86                         if (xmlMembersMapping.HasWrapperElement && exportEnclosingType)
87                         {
88                                 XmlSchema schema = GetSchema (xmlMembersMapping.Namespace);
89                                 XmlSchemaComplexType stype = new XmlSchemaComplexType ();
90         
91                                 XmlSchemaSequence particle;
92                                 XmlSchemaAnyAttribute anyAttribute;
93                                 ExportMembersMapSchema (schema, cmap, null, stype.Attributes, out particle, out anyAttribute);
94                                 stype.Particle = particle;
95                                 stype.AnyAttribute = anyAttribute;
96                                 
97                                 if (encodedFormat)
98                                 {
99                                         stype.Name = xmlMembersMapping.ElementName;
100                                         schema.Items.Add (stype);
101                                 }
102                                 else
103                                 {
104                                         XmlSchemaElement selem = new XmlSchemaElement ();
105                                         selem.Name = xmlMembersMapping.ElementName;
106                                         selem.SchemaType = stype;
107                                         schema.Items.Add (selem);
108                                 }
109                         }
110                         else
111                         {
112                                 ICollection members = cmap.ElementMembers;
113                                 if (members != null)
114                                 {
115                                         foreach (XmlTypeMapMemberElement member in members)
116                                         {
117                                                 if (member is XmlTypeMapMemberAnyElement && member.TypeData.IsListType)
118                                                 {
119                                                         XmlSchema mschema = GetSchema (xmlMembersMapping.Namespace);
120                                                         XmlSchemaParticle par = GetSchemaArrayElement (mschema, member.ElementInfo);
121                                                         if (par is XmlSchemaAny)
122                                                         {
123                                                                 XmlSchemaComplexType ct = FindComplexType (mschema.Items, "any");
124                                                                 if (ct != null) continue;
125                                                                 
126                                                                 ct = new XmlSchemaComplexType ();
127                                                                 ct.Name = "any";
128                                                                 ct.IsMixed = true;
129                                                                 XmlSchemaSequence seq = new XmlSchemaSequence ();
130                                                                 ct.Particle = seq;
131                                                                 seq.Items.Add (par);
132                                                                 mschema.Items.Add (ct);
133                                                                 continue;
134                                                         }
135                                                 }
136                                                 
137                                                 
138                                                 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo [0];
139                                                 XmlSchema schema;
140
141                                                 if (encodedFormat)
142                                                 {
143                                                         schema = GetSchema (xmlMembersMapping.Namespace);
144                                                         ImportNamespace (schema, XmlSerializer.EncodingNamespace);
145                                                 }
146                                                 else
147                                                         schema = GetSchema (einfo.Namespace);
148                                                 
149                                                 
150                                                 XmlSchemaElement exe = FindElement (schema.Items, einfo.ElementName);
151                                                 XmlSchemaElement elem;
152                                                 
153                                                 Type memType = member.GetType();
154                                                 if (member is XmlTypeMapMemberFlatList)
155                                                         throw new InvalidOperationException ("Unwrapped arrays not supported as parameters");
156                                                 else if (memType == typeof(XmlTypeMapMemberElement))
157                                                         elem = (XmlSchemaElement) GetSchemaElement (schema, einfo, member.DefaultValue, false);
158                                                 else
159                                                         elem = (XmlSchemaElement) GetSchemaElement (schema, einfo, false);
160                                                 
161                                                 // In encoded format, the schema elements are not needed
162                                                 if (!encodedFormat)
163                                                         schema.Items.Add (elem);
164                                                 
165                                                 if (exe != null)
166                                                 {
167                                                         if (exe.SchemaTypeName.Equals (elem.SchemaTypeName))
168                                                                 schema.Items.Remove (elem);
169                                                         else
170                                                         {
171                                                                 string s = "The XML element named '" + einfo.ElementName + "' ";
172                                                                 s += "from namespace '" + schema.TargetNamespace + "' references distinct types " + elem.SchemaTypeName.Name + " and " + exe.SchemaTypeName.Name + ". ";
173                                                                 s += "Use XML attributes to specify another XML name or namespace for the element or types.";
174                                                                 throw new InvalidOperationException (s);
175                                                         }
176                                                 }
177                                         }
178                                 }
179                         }
180                         
181                         CompileSchemas ();
182                 }
183
184                 [MonoTODO]
185                 public XmlQualifiedName ExportTypeMapping (XmlMembersMapping xmlMembersMapping)
186                 {
187                         throw new NotImplementedException ();
188                 }
189
190                 public void ExportTypeMapping (XmlTypeMapping xmlTypeMapping)
191                 {
192                         if (!xmlTypeMapping.IncludeInSchema) return;
193                         if (IsElementExported (xmlTypeMapping)) return;
194                         
195                         if (encodedFormat)
196                         {
197                                 ExportClassSchema (xmlTypeMapping);
198                                 XmlSchema schema = GetSchema (xmlTypeMapping.XmlTypeNamespace);
199                                 ImportNamespace (schema, XmlSerializer.EncodingNamespace);
200                         }
201                         else
202                         {
203                                 XmlSchema schema = GetSchema (xmlTypeMapping.Namespace);
204                                 XmlTypeMapElementInfo einfo = new XmlTypeMapElementInfo (null, xmlTypeMapping.TypeData);
205                                 einfo.Namespace = xmlTypeMapping.Namespace;
206                                 einfo.ElementName = xmlTypeMapping.ElementName;
207                                 if (xmlTypeMapping.TypeData.IsComplexType)
208                                         einfo.MappedType = xmlTypeMapping;
209                                 einfo.IsNullable = false;
210                                 schema.Items.Add (GetSchemaElement (schema, einfo, false));
211                                 SetElementExported (xmlTypeMapping);
212                         }
213                         
214                         CompileSchemas ();
215                 }
216
217                 void ExportClassSchema (XmlTypeMapping map)
218                 {
219                         if (IsMapExported (map)) return;
220                         SetMapExported (map);
221                         
222                         if (map.TypeData.Type == typeof(object))
223                         {
224                                 foreach (XmlTypeMapping dmap in map.DerivedTypes)
225                                         if (dmap.TypeData.SchemaType == SchemaTypes.Class) ExportClassSchema (dmap);
226                                 return;
227                         }
228
229                         XmlSchema schema = GetSchema (map.XmlTypeNamespace);
230                         XmlSchemaComplexType stype = new XmlSchemaComplexType ();
231                         stype.Name = map.XmlType;
232                         schema.Items.Add (stype);
233
234                         ClassMap cmap = (ClassMap)map.ObjectMap;
235
236                         if (cmap.HasSimpleContent)
237                         {
238                                 XmlSchemaSimpleContent simple = new XmlSchemaSimpleContent ();
239                                 stype.ContentModel = simple;
240                                 XmlSchemaSimpleContentExtension ext = new XmlSchemaSimpleContentExtension ();
241                                 simple.Content = ext;
242                                 XmlSchemaSequence particle;
243                                 XmlSchemaAnyAttribute anyAttribute;
244                                 ExportMembersMapSchema (schema, cmap, map.BaseMap, ext.Attributes, out particle, out anyAttribute);
245                                 ext.AnyAttribute = anyAttribute;
246                                 if (map.BaseMap == null)
247                                         ext.BaseTypeName = cmap.SimpleContentBaseType;
248                                 else {
249                                         ext.BaseTypeName = new XmlQualifiedName (map.BaseMap.XmlType, map.BaseMap.XmlTypeNamespace);
250                                         ImportNamespace (schema, map.BaseMap.XmlTypeNamespace);
251                                         ExportClassSchema (map.BaseMap);
252                                 }
253                         }
254                         else if (map.BaseMap != null && map.BaseMap.IncludeInSchema)
255                         {
256                                 XmlSchemaComplexContent cstype = new XmlSchemaComplexContent ();
257                                 XmlSchemaComplexContentExtension ext = new XmlSchemaComplexContentExtension ();
258                                 ext.BaseTypeName = new XmlQualifiedName (map.BaseMap.XmlType, map.BaseMap.XmlTypeNamespace);
259                                 cstype.Content = ext;
260                                 stype.ContentModel = cstype;
261
262                                 XmlSchemaSequence particle;
263                                 XmlSchemaAnyAttribute anyAttribute;
264                                 ExportMembersMapSchema (schema, cmap, map.BaseMap, ext.Attributes, out particle, out anyAttribute);
265                                 ext.Particle = particle;
266                                 ext.AnyAttribute = anyAttribute;
267                                 stype.IsMixed = HasMixedContent (map);
268                                 cstype.IsMixed = BaseHasMixedContent (map);
269
270                                 ImportNamespace (schema, map.BaseMap.XmlTypeNamespace);
271                                 ExportClassSchema (map.BaseMap);
272                         }
273                         else
274                         {
275                                 XmlSchemaSequence particle;
276                                 XmlSchemaAnyAttribute anyAttribute;
277                                 ExportMembersMapSchema (schema, cmap, map.BaseMap, stype.Attributes, out particle, out anyAttribute);
278                                 stype.Particle = particle;
279                                 stype.AnyAttribute = anyAttribute;
280                                 stype.IsMixed = cmap.XmlTextCollector != null;
281                         }
282                         
283                         foreach (XmlTypeMapping dmap in map.DerivedTypes)
284                                 if (dmap.TypeData.SchemaType == SchemaTypes.Class) ExportClassSchema (dmap);
285                 }
286                 
287                 bool BaseHasMixedContent (XmlTypeMapping map)
288                 {
289                         ClassMap cmap = (ClassMap)map.ObjectMap;
290                         return (cmap.XmlTextCollector != null && (map.BaseMap != null && DefinedInBaseMap (map.BaseMap, cmap.XmlTextCollector)));
291                 }
292
293                 bool HasMixedContent (XmlTypeMapping map)
294                 {
295                         ClassMap cmap = (ClassMap)map.ObjectMap;
296                         return (cmap.XmlTextCollector != null && (map.BaseMap == null || !DefinedInBaseMap (map.BaseMap, cmap.XmlTextCollector)));
297                 }
298
299                 void ExportMembersMapSchema (XmlSchema schema, ClassMap map, XmlTypeMapping baseMap, XmlSchemaObjectCollection outAttributes, out XmlSchemaSequence particle, out XmlSchemaAnyAttribute anyAttribute)
300                 {
301                         particle = null;
302                         XmlSchemaSequence seq = new XmlSchemaSequence ();
303
304                         ICollection members = map.ElementMembers;
305                         if (members != null && !map.HasSimpleContent)
306                         {
307                                 foreach (XmlTypeMapMemberElement member in members)
308                                 {
309                                         if (baseMap != null && DefinedInBaseMap (baseMap, member)) continue;
310
311                                         Type memType = member.GetType();
312                                         if (memType == typeof(XmlTypeMapMemberFlatList))
313                                         {
314                                                 XmlSchemaParticle part = GetSchemaArrayElement (schema, member.ElementInfo);
315                                                 if (part != null) seq.Items.Add (part);
316                                         }
317                                         else if (memType == typeof(XmlTypeMapMemberAnyElement))
318                                         {
319                                                 seq.Items.Add (GetSchemaArrayElement (schema, member.ElementInfo));
320                                         }
321                                         else if (memType == typeof(XmlTypeMapMemberElement))
322                                         {
323                                                 XmlSchemaParticle elem = GetSchemaElement (schema, (XmlTypeMapElementInfo) member.ElementInfo [0], member.DefaultValue, true);
324                                                 if (elem != null)
325                                                         seq.Items.Add (elem);
326                                         }
327                                         else
328                                         {
329                                                 seq.Items.Add (GetSchemaElement (schema, (XmlTypeMapElementInfo) member.ElementInfo [0], true));
330                                         }
331                                 }
332                         }
333
334                         if (seq.Items.Count > 0)
335                                 particle = seq;
336
337                         // Write attributes
338
339                         ICollection attributes = map.AttributeMembers;
340                         if (attributes != null)
341                         {
342                                 foreach (XmlTypeMapMemberAttribute attr in attributes) {
343                                         if (baseMap != null && DefinedInBaseMap (baseMap, attr)) continue;
344                                         outAttributes.Add (GetSchemaAttribute (schema, attr, true));
345                                 }
346                         }
347
348                         XmlTypeMapMember anyAttrMember = map.DefaultAnyAttributeMember;
349                         if (anyAttrMember != null)
350                                 anyAttribute = new XmlSchemaAnyAttribute ();
351                         else
352                                 anyAttribute = null;
353                 }
354                 
355                 XmlSchemaElement FindElement (XmlSchemaObjectCollection col, string name)
356                 {
357                         foreach (XmlSchemaObject ob in col)
358                         {
359                                 XmlSchemaElement elem = ob as XmlSchemaElement;
360                                 if (elem != null && elem.Name == name) return elem;
361                         }
362                         return null;
363                 }
364
365                 XmlSchemaComplexType FindComplexType (XmlSchemaObjectCollection col, string name)
366                 {
367                         foreach (XmlSchemaObject ob in col)
368                         {
369                                 XmlSchemaComplexType ctype = ob as XmlSchemaComplexType;
370                                 if (ctype != null && ctype.Name == name) return ctype;
371                         }
372                         return null;
373                 }
374
375                 XmlSchemaAttribute GetSchemaAttribute (XmlSchema currentSchema, XmlTypeMapMemberAttribute attinfo, bool isTypeMember)
376                 {
377                         XmlSchemaAttribute sat = new XmlSchemaAttribute ();
378                         if (attinfo.DefaultValue != System.DBNull.Value) sat.DefaultValue = XmlCustomFormatter.ToXmlString (attinfo.TypeData, attinfo.DefaultValue);
379
380                         ImportNamespace (currentSchema, attinfo.Namespace);
381
382                         XmlSchema memberSchema;
383                         if (attinfo.Namespace.Length == 0 && attinfo.Form != XmlSchemaForm.Qualified)
384                                 memberSchema = currentSchema;
385                         else
386                                 memberSchema = GetSchema (attinfo.Namespace);
387
388                         if (currentSchema == memberSchema || encodedFormat)
389                         {
390                                 sat.Name = attinfo.AttributeName;
391                                 if (isTypeMember) sat.Form = attinfo.Form;
392                                 if (attinfo.TypeData.SchemaType == SchemaTypes.Enum)
393                                 {
394                                         ImportNamespace (currentSchema, attinfo.DataTypeNamespace);
395                                         ExportEnumSchema (attinfo.MappedType);
396                                         sat.SchemaTypeName = new XmlQualifiedName (attinfo.TypeData.XmlType, attinfo.DataTypeNamespace);;
397                                 }
398                                 else if (attinfo.TypeData.SchemaType == SchemaTypes.Array && TypeTranslator.IsPrimitive (attinfo.TypeData.ListItemType))
399                                 {
400                                         sat.SchemaType = GetSchemaSimpleListType (attinfo.TypeData);
401                                 }
402                                 else
403                                         sat.SchemaTypeName = new XmlQualifiedName (attinfo.TypeData.XmlType, attinfo.DataTypeNamespace);;
404                         }
405                         else
406                         {
407                                 sat.RefName = new XmlQualifiedName (attinfo.AttributeName, attinfo.Namespace);
408                                 foreach (XmlSchemaObject ob in memberSchema.Items)
409                                         if (ob is XmlSchemaAttribute && ((XmlSchemaAttribute)ob).Name == attinfo.AttributeName)
410                                                 return sat;
411                                                 
412                                 memberSchema.Items.Add (GetSchemaAttribute (memberSchema, attinfo, false));
413                         }
414                         return sat;
415                 }
416
417                 XmlSchemaParticle GetSchemaElement (XmlSchema currentSchema, XmlTypeMapElementInfo einfo, bool isTypeMember)
418                 {
419                         return GetSchemaElement (currentSchema, einfo, System.DBNull.Value, isTypeMember);
420                 }
421                 
422                 XmlSchemaParticle GetSchemaElement (XmlSchema currentSchema, XmlTypeMapElementInfo einfo, object defaultValue, bool isTypeMember)
423                 {
424                         if (einfo.IsTextElement) return null;
425
426                         if (einfo.IsUnnamedAnyElement)
427                         {
428                                 XmlSchemaAny any = new XmlSchemaAny ();
429                                 any.MinOccurs = 0;
430                                 any.MaxOccurs = 1;
431                                 return any;
432                         }
433                         
434                         XmlSchemaElement selem = new XmlSchemaElement ();
435
436                         if (isTypeMember)
437                         {
438                                 selem.MaxOccurs = 1;
439                                 selem.MinOccurs = einfo.IsNullable ? 1 : 0;
440                                 
441                                 if ((einfo.TypeData.IsValueType && einfo.Member != null && !einfo.Member.IsOptionalValueType) || encodedFormat) 
442                                         selem.MinOccurs = 1;
443                         }
444
445                         XmlSchema memberSchema = null;
446                         
447                         if (!encodedFormat)
448                         {
449                                 memberSchema = GetSchema (einfo.Namespace);
450                                 ImportNamespace (currentSchema, einfo.Namespace);
451                         }
452                         
453                         if (currentSchema == memberSchema || encodedFormat || !isTypeMember)
454                         {
455                                 if (isTypeMember) selem.IsNillable = einfo.IsNullable;
456                                 selem.Name = einfo.ElementName;
457
458                                 if (defaultValue != System.DBNull.Value)
459                                         selem.DefaultValue = XmlCustomFormatter.ToXmlString (einfo.TypeData, defaultValue);
460                                         
461                                 if (einfo.Form != XmlSchemaForm.Qualified)
462                                         selem.Form = einfo.Form;
463
464                                 switch (einfo.TypeData.SchemaType)
465                                 {
466                                         case SchemaTypes.XmlNode: 
467                                                 selem.SchemaType = GetSchemaXmlNodeType ();
468                                                 break;
469
470                                         case SchemaTypes.XmlSerializable:
471                                                 selem.SchemaType = GetSchemaXmlSerializableType ();
472                                                 break;
473
474                                         case SchemaTypes.Enum:
475                                                 selem.SchemaTypeName = new XmlQualifiedName (einfo.MappedType.XmlType, einfo.MappedType.XmlTypeNamespace);
476                                                 ImportNamespace (currentSchema, einfo.MappedType.XmlTypeNamespace);
477                                                 ExportEnumSchema (einfo.MappedType);
478                                                 break;
479
480                                         case SchemaTypes.Array: 
481                                                 XmlQualifiedName atypeName = ExportArraySchema (einfo.MappedType, currentSchema.TargetNamespace); 
482                                                 selem.SchemaTypeName = atypeName;
483                                                 ImportNamespace (currentSchema, atypeName.Namespace);
484                                                 break;
485
486                                         case SchemaTypes.Class:
487                                                 if (einfo.MappedType.TypeData.Type != typeof(object)) {
488                                                         selem.SchemaTypeName = new XmlQualifiedName (einfo.MappedType.XmlType, einfo.MappedType.XmlTypeNamespace);
489                                                         ImportNamespace (currentSchema, einfo.MappedType.XmlTypeNamespace);
490                                                 }
491                                                 else if (encodedFormat)
492                                                         selem.SchemaTypeName = new XmlQualifiedName (einfo.MappedType.XmlType, einfo.MappedType.XmlTypeNamespace);
493                                                         
494                                                 ExportClassSchema (einfo.MappedType);
495                                                 break;
496
497                                         case SchemaTypes.Primitive:
498                                                 selem.SchemaTypeName = new XmlQualifiedName (einfo.TypeData.XmlType, einfo.DataTypeNamespace);;
499                                                 break;
500                                 }
501                         }
502                         else
503                         {
504                                 selem.RefName = new XmlQualifiedName (einfo.ElementName, einfo.Namespace);
505                                 foreach (XmlSchemaObject ob in memberSchema.Items)
506                                         if (ob is XmlSchemaElement && ((XmlSchemaElement)ob).Name == einfo.ElementName)
507                                                 return selem;
508                                                 
509                                 memberSchema.Items.Add (GetSchemaElement (memberSchema, einfo, defaultValue, false));
510                         }
511                         return selem;
512                 }
513
514                 void ImportNamespace (XmlSchema schema, string ns)
515                 {
516                         if (ns == "" || ns == schema.TargetNamespace || ns == XmlSchema.Namespace) return;
517
518                         foreach (XmlSchemaObject sob in schema.Includes)
519                                 if ((sob is XmlSchemaImport) && ((XmlSchemaImport)sob).Namespace == ns) return;
520
521                         XmlSchemaImport imp = new XmlSchemaImport ();
522                         imp.Namespace = ns;
523                         schema.Includes.Add (imp);
524                 }
525
526                 bool DefinedInBaseMap (XmlTypeMapping map, XmlTypeMapMember member)
527                 {
528                         if (((ClassMap)map.ObjectMap).FindMember (member.Name) != null)
529                                 return true;
530                         else if (map.BaseMap != null)
531                                 return DefinedInBaseMap (map.BaseMap, member);
532                         else
533                                 return false;
534                 }
535
536                 XmlSchemaType GetSchemaXmlNodeType ()
537                 {
538                         XmlSchemaComplexType stype = new XmlSchemaComplexType ();
539                         stype.IsMixed = true;
540                         XmlSchemaSequence seq = new XmlSchemaSequence ();
541                         seq.Items.Add (new XmlSchemaAny ());
542                         stype.Particle = seq;
543                         return stype;
544                 }
545
546                 XmlSchemaType GetSchemaXmlSerializableType ()
547                 {
548                         XmlSchemaComplexType stype = new XmlSchemaComplexType ();
549                         XmlSchemaSequence seq = new XmlSchemaSequence ();
550                         XmlSchemaElement selem = new XmlSchemaElement ();
551                         selem.RefName = new XmlQualifiedName ("schema",XmlSchema.Namespace);
552                         seq.Items.Add (selem);
553                         seq.Items.Add (new XmlSchemaAny ());
554                         stype.Particle = seq;
555                         return stype;
556                 }
557
558                 XmlSchemaSimpleType GetSchemaSimpleListType (TypeData typeData)
559                 {
560                         XmlSchemaSimpleType stype = new XmlSchemaSimpleType ();
561                         XmlSchemaSimpleTypeList list = new XmlSchemaSimpleTypeList ();
562                         TypeData itemTypeData = TypeTranslator.GetTypeData (typeData.ListItemType);
563                         list.ItemTypeName = new XmlQualifiedName (itemTypeData.XmlType, XmlSchema.Namespace);
564                         stype.Content = list;
565                         return stype;
566                 }
567
568                 XmlSchemaParticle GetSchemaArrayElement (XmlSchema currentSchema, XmlTypeMapElementInfoList infos)
569                 {
570                         int numInfos = infos.Count;
571                         if (numInfos > 0 && ((XmlTypeMapElementInfo)infos[0]).IsTextElement) numInfos--;
572                         if (numInfos == 0) return null;
573
574                         if (numInfos == 1)
575                         {
576                                 XmlSchemaParticle selem = GetSchemaElement (currentSchema, (XmlTypeMapElementInfo) infos[infos.Count-1], true);
577                                 selem.MinOccursString = "0";
578                                 selem.MaxOccursString = "unbounded";
579                                 return selem;
580                         }
581                         else
582                         {
583                                 XmlSchemaChoice schoice = new XmlSchemaChoice ();
584                                 schoice.MinOccursString = "0";
585                                 schoice.MaxOccursString = "unbounded";
586                                 foreach (XmlTypeMapElementInfo einfo in infos)
587                                 {
588                                         if (einfo.IsTextElement) continue;
589                                         schoice.Items.Add (GetSchemaElement (currentSchema, einfo, true));
590                                 }
591                                 return schoice;
592                         }
593                 }
594
595                 void ExportEnumSchema (XmlTypeMapping map)
596                 {
597                         if (IsMapExported (map)) return;
598                         SetMapExported (map);
599
600                         XmlSchema schema = GetSchema (map.XmlTypeNamespace);
601                         XmlSchemaSimpleType stype = new XmlSchemaSimpleType ();
602                         stype.Name = map.ElementName;
603                         schema.Items.Add (stype);
604
605                         XmlSchemaSimpleTypeRestriction rest = new XmlSchemaSimpleTypeRestriction ();
606                         rest.BaseTypeName = new XmlQualifiedName ("string",XmlSchema.Namespace);
607                         EnumMap emap = (EnumMap) map.ObjectMap;
608
609                         foreach (EnumMap.EnumMapMember emem in emap.Members)
610                         {
611                                 XmlSchemaEnumerationFacet ef = new XmlSchemaEnumerationFacet ();
612                                 ef.Value = emem.XmlName;
613                                 rest.Facets.Add (ef);
614                         }
615                         stype.Content = rest;
616                 }
617
618                 XmlQualifiedName ExportArraySchema (XmlTypeMapping map, string defaultNamespace)
619                 {
620                         ListMap lmap = (ListMap) map.ObjectMap;
621
622                         if (encodedFormat)
623                         {
624                                 string name, ns, schemaNs;
625                                 lmap.GetArrayType (-1, out name, out ns);                               
626                                 if (ns == XmlSchema.Namespace) schemaNs = defaultNamespace;
627                                 else schemaNs = ns;
628
629                                 if (IsMapExported (map)) return new XmlQualifiedName (lmap.GetSchemaArrayName (), schemaNs);
630                                 SetMapExported (map);
631
632                                 XmlSchema schema = GetSchema (schemaNs);
633                                 XmlSchemaComplexType stype = new XmlSchemaComplexType ();
634                                 stype.Name = lmap.GetSchemaArrayName ();
635                                 schema.Items.Add (stype);
636                                 
637                                 XmlSchemaComplexContent content = new XmlSchemaComplexContent();
638                                 content.IsMixed = false;
639                                 stype.ContentModel = content;
640                                 
641                                 XmlSchemaComplexContentRestriction rest = new XmlSchemaComplexContentRestriction ();
642                                 content.Content = rest;
643                                 rest.BaseTypeName = new XmlQualifiedName ("Array", XmlSerializer.EncodingNamespace);
644                                 XmlSchemaAttribute at = new XmlSchemaAttribute ();
645                                 rest.Attributes.Add (at);
646                                 at.RefName = new XmlQualifiedName ("arrayType", XmlSerializer.EncodingNamespace);
647                                 
648                                 XmlAttribute arrayType = Document.CreateAttribute ("arrayType", XmlSerializer.WsdlNamespace);
649                                 arrayType.Value = ns + (ns != "" ? ":" : "") + name;
650                                 at.UnhandledAttributes = new XmlAttribute [] { arrayType };
651                                 ImportNamespace (schema, XmlSerializer.WsdlNamespace);
652                         
653                                 XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) lmap.ItemInfo[0];
654                                 if (einfo.MappedType != null)
655                                 {
656                                         switch (einfo.TypeData.SchemaType)
657                                         {
658                                                 case SchemaTypes.Enum:
659                                                         ExportEnumSchema (einfo.MappedType);
660                                                         break;
661                                                 case SchemaTypes.Array: 
662                                                         ExportArraySchema (einfo.MappedType, schemaNs); 
663                                                         break;
664                                                 case SchemaTypes.Class:
665                                                         ExportClassSchema (einfo.MappedType);
666                                                         break;
667                                         }
668                                 }
669                                 
670                                 return new XmlQualifiedName (lmap.GetSchemaArrayName (), schemaNs);
671                         }
672                         else
673                         {
674                                 if (IsMapExported (map)) return new XmlQualifiedName (map.XmlType, map.XmlTypeNamespace);
675                                 
676                                 SetMapExported (map);
677                                 XmlSchema schema = GetSchema (map.XmlTypeNamespace);
678                                 XmlSchemaComplexType stype = new XmlSchemaComplexType ();
679                                 stype.Name = map.ElementName;
680                                 schema.Items.Add (stype);
681
682                                 XmlSchemaParticle spart = GetSchemaArrayElement (schema, lmap.ItemInfo);
683                                 if (spart is XmlSchemaChoice)
684                                         stype.Particle = spart;
685                                 else
686                                 {
687                                         XmlSchemaSequence seq = new XmlSchemaSequence ();
688                                         seq.Items.Add (spart);
689                                         stype.Particle = seq;
690                                 }
691                                         
692                                 return new XmlQualifiedName (map.XmlType, map.XmlTypeNamespace);
693                         }
694                 }
695                 
696                 XmlDocument Document
697                 {
698                         get
699                         {
700                                 if (xmlDoc == null) xmlDoc = new XmlDocument ();
701                                 return xmlDoc;
702                         }
703                 }
704
705                 bool IsMapExported (XmlTypeMapping map)
706                 {
707                         if (exportedMaps.ContainsKey (GetMapKey(map))) return true;
708                         return false;
709                 }
710
711                 void SetMapExported (XmlTypeMapping map)
712                 {
713                         exportedMaps [GetMapKey(map)] = map;
714                 }
715
716                 bool IsElementExported (XmlTypeMapping map)
717                 {
718                         if (exportedElements.ContainsKey (GetMapKey(map))) return true;
719                         if (map.TypeData.Type == typeof(object)) return true;
720                         return false;
721                 }
722
723                 void SetElementExported (XmlTypeMapping map)
724                 {
725                         exportedElements [GetMapKey(map)] = map;
726                 }
727                 
728                 string GetMapKey (XmlTypeMapping map)
729                 {
730                         // Don't use type name for array types, since we can have different
731                         // classes that represent the same array type (for example
732                         // StringCollection and string[]).
733                         
734                         if (map.TypeData.IsListType)
735                                 return GetArrayKeyName (map.TypeData) + " " + map.XmlType + " " + map.XmlTypeNamespace;
736                         else
737                                 return map.TypeData.FullTypeName + " " + map.XmlType + " " + map.XmlTypeNamespace;
738                 }
739                 
740                 string GetArrayKeyName (TypeData td)
741                 {
742                         TypeData etd = td.ListItemTypeData;
743                         return "*arrayof*" + (etd.IsListType ? GetArrayKeyName (etd) : etd.FullTypeName);
744                 }
745                 
746                 void CompileSchemas ()
747                 {
748 //                      foreach (XmlSchema sc in schemas)
749 //                              sc.Compile (null);
750                 }
751
752                 XmlSchema GetSchema (string ns)
753                 {
754                         XmlSchema schema = schemas [ns];
755                         if (schema == null)
756                         {
757                                 schema = new XmlSchema ();
758                                 schema.TargetNamespace = ns;
759                                 if (!encodedFormat)
760                                         schema.ElementFormDefault = XmlSchemaForm.Qualified;
761                                 schemas.Add (schema);
762                         }
763                         return schema;
764                 }
765
766                 #endregion // Methods
767         }
768 }