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