2004-11-18 Lluis Sanchez Gual <lluis@novell.com>
[mono.git] / mcs / class / System.XML / System.Xml.Schema / XmlSchemaInference.cs
1 //
2 // XmlSchemaInference.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C)2004 Novell Inc.
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 #if NET_2_0
32
33 using System;
34 using System.Collections;
35 using System.Xml;
36 using System.Xml.Schema;
37
38 using QName = System.Xml.XmlQualifiedName;
39 using Form = System.Xml.Schema.XmlSchemaForm;
40 using Use = System.Xml.Schema.XmlSchemaUse;
41 using SOMList = System.Xml.Schema.XmlSchemaObjectCollection;
42 using SOMObject = System.Xml.Schema.XmlSchemaObject;
43 using Element = System.Xml.Schema.XmlSchemaElement;
44 using Attr = System.Xml.Schema.XmlSchemaAttribute;
45 using AttrGroup = System.Xml.Schema.XmlSchemaAttributeGroup;
46 using AttrGroupRef = System.Xml.Schema.XmlSchemaAttributeGroupRef;
47 using SimpleType = System.Xml.Schema.XmlSchemaSimpleType;
48 using ComplexType = System.Xml.Schema.XmlSchemaComplexType;
49 using SimpleModel = System.Xml.Schema.XmlSchemaSimpleContent;
50 using SimpleExt = System.Xml.Schema.XmlSchemaSimpleContentExtension;
51 using SimpleRst = System.Xml.Schema.XmlSchemaSimpleContentRestriction;
52 using ComplexModel = System.Xml.Schema.XmlSchemaComplexContent;
53 using ComplexExt = System.Xml.Schema.XmlSchemaComplexContentExtension;
54 using ComplexRst = System.Xml.Schema.XmlSchemaComplexContentRestriction;
55 using SimpleTypeRst = System.Xml.Schema.XmlSchemaSimpleTypeRestriction;
56 using SimpleList = System.Xml.Schema.XmlSchemaSimpleTypeList;
57 using SimpleUnion = System.Xml.Schema.XmlSchemaSimpleTypeUnion;
58 using SchemaFacet = System.Xml.Schema.XmlSchemaFacet;
59 using LengthFacet = System.Xml.Schema.XmlSchemaLengthFacet;
60 using MinLengthFacet = System.Xml.Schema.XmlSchemaMinLengthFacet;
61 using Particle = System.Xml.Schema.XmlSchemaParticle;
62 using Sequence = System.Xml.Schema.XmlSchemaSequence;
63 using Choice = System.Xml.Schema.XmlSchemaChoice;
64
65
66 namespace System.Xml.Schema
67 {
68         [MonoTODO ("merge primitive types; infer gYearMonth too; in some cases sequence should contain element whose minOccurs=0 (no obvious rules right now); reject some non-supported schema components")]
69         public class XmlSchemaInference
70         {
71                 public enum InferenceOption {
72                         Restricted,
73                         Relaxed,
74                 }
75
76                 InferenceOption occurrence = InferenceOption.Restricted;
77                 InferenceOption typeInference = InferenceOption.Restricted;
78
79                 public XmlSchemaInference ()
80                 {
81                 }
82
83                 public InferenceOption Occurrence {
84                         get { return occurrence; }
85                         set { occurrence = value; }
86                 }
87
88                 public InferenceOption TypeInference {
89                         get { return TypeInference; }
90                         set { typeInference = value; }
91                 }
92
93                 public XmlSchemaSet InferSchema (XmlReader xmlReader)
94                 {
95                         return InferSchema (xmlReader, new XmlSchemaSet ());
96                 }
97
98                 public XmlSchemaSet InferSchema (XmlReader xmlReader,
99                         XmlSchemaSet schemas)
100                 {
101                         return XsdInference.Process (xmlReader, schemas,
102                                 occurrence == InferenceOption.Relaxed,
103                                 typeInference == InferenceOption.Relaxed);
104                 }
105         }
106
107         class XsdInference
108         {
109                 public static XmlSchemaSet Process (XmlReader xmlReader, 
110                         XmlSchemaSet schemas,
111                         bool laxOccurence,
112                         bool laxTypeInference)
113                 {
114                         XsdInference impl = new XsdInference (xmlReader,
115                                 schemas, laxOccurence, laxTypeInference);
116                         impl.Run ();
117                         return impl.schemas;
118                 }
119
120                 public const string NamespaceXml = "http://www.w3.org/XML/1998/namespace";
121
122                 public const string NamespaceXmlns = "http://www.w3.org/2000/xmlns/";
123
124                 public const string XdtNamespace = "http://www.w3.org/2003/11/xpath-datatypes";
125
126                 static readonly QName QNameString = new QName ("string",
127                         XmlSchema.Namespace);
128
129                 static readonly QName QNameBoolean = new QName ("boolean",
130                         XmlSchema.Namespace);
131
132                 static readonly QName QNameAnyType = new QName ("anyType",
133                         XmlSchema.Namespace);
134
135                 XmlReader source;
136                 XmlSchemaSet schemas;
137                 bool laxOccurence;
138                 bool laxTypeInference;
139
140                 Hashtable newElements = new Hashtable ();
141                 Hashtable newAttributes = new Hashtable ();
142
143                 private XsdInference (XmlReader xmlReader, 
144                         XmlSchemaSet schemas, 
145                         bool laxOccurence, 
146                         bool laxTypeInference)
147                 {
148                         this.source = xmlReader;
149                         this.schemas = schemas;
150                         this.laxOccurence = laxOccurence;
151                         this.laxTypeInference = laxTypeInference;
152                 }
153
154                 private void Run ()
155                 {
156                         // XmlSchemaSet need to be compiled.
157                         schemas.Compile ();
158
159                         // move to top-level element
160                         source.MoveToContent ();
161                         int depth = source.Depth;
162                         if (source.NodeType != XmlNodeType.Element)
163                                 throw new ArgumentException ("Argument XmlReader content is expected to be an element.");
164
165                         QName qname = new QName (source.LocalName,
166                                 source.NamespaceURI);
167                         Element el = GetGlobalElement (qname);
168                         if (el == null) {
169                                 el = CreateGlobalElement (qname);
170                                 InferElement (el, qname.Namespace, true);
171                         }
172                         else
173                                 InferElement (el, qname.Namespace, false);
174                 }
175
176                 private void IncludeXmlAttributes ()
177                 {
178                         if (schemas.Schemas (NamespaceXml).Count == 0)
179                                 // FIXME: do it from resources.
180                                 schemas.Add (NamespaceXml, 
181                                         "http://www.w3.org/2001/xml.xsd");
182                 }
183
184                 private void InferElement (Element el, string ns, bool isNew)
185                 {
186                         // Quick check for reference to another definition
187                         // (i.e. element ref='...' that should be redirected)
188                         if (el.RefName != QName.Empty) {
189                                 Element body = GetGlobalElement (el.RefName);
190                                 if (body == null) {
191                                         body = CreateElement (el.RefName);
192                                         InferElement (body, ns, true);
193                                 }
194                                 else
195                                         InferElement (body, ns, isNew);
196                                 return;
197                         }
198
199                         // Attributes
200                         if (source.MoveToFirstAttribute ()) {
201                                 InferAttributes (el, ns, isNew);
202                                 source.MoveToElement ();
203                         }
204
205                         // Content
206                         if (source.IsEmptyElement) {
207                                 InferAsEmptyElement (el, ns, isNew);
208                                 source.Read ();
209                                 source.MoveToContent ();
210                         }
211                         else {
212                                 InferContent (el, ns, isNew);
213                                 source.ReadEndElement ();
214                         }
215                         if (el.SchemaType == null &&
216                                 el.SchemaTypeName == QName.Empty)
217                                 el.SchemaTypeName = QNameString;
218                 }
219
220                 #region Attribute Inference
221
222                 private Hashtable CollectAttrTable (SOMList attList)
223                 {
224                         // get attribute definition table.
225                         Hashtable table = new Hashtable ();
226                         foreach (XmlSchemaObject obj in attList) {
227                                 Attr attr = obj as Attr;
228                                 if (attr == null)
229                                         throw Error (obj, String.Format ("Attribute inference only supports direct attribute definition. {0} is not supported.", obj.GetType ()));
230                                 if (attr.RefName != QName.Empty)
231                                         table.Add (attr.RefName, attr);
232                                 else
233                                         table.Add (new QName (attr.Name, ""),
234                                                 attr);
235                         }
236                         return table;
237                 }
238
239                 private void InferAttributes (Element el, string ns, bool isNew)
240                 {
241                         // Now this element is going to have complexType.
242                         // It currently not, then we have to replace it.
243                         ComplexType ct = null;
244                         SOMList attList = null;
245                         Hashtable table = null;
246
247                         do {
248                                 switch (source.NamespaceURI) {
249                                 case NamespaceXml:
250                                         if (schemas.Schemas (
251                                                 NamespaceXml) .Count == 0)
252                                                 IncludeXmlAttributes ();
253                                         break;
254                                 case XmlSchema.InstanceNamespace:
255                                         if (source.LocalName == "nil")
256                                                 el.IsNillable = true;
257                                         // all other xsi:* atts are ignored
258                                         continue;
259                                 case NamespaceXmlns:
260                                         continue;
261                                 }
262                                 if (ct == null) {
263                                         ct = ToComplexType (el);
264                                         attList = GetAttributes (ct);
265                                         table = CollectAttrTable (attList);
266                                 }
267                                 QName attrName = new QName (
268                                         source.LocalName, source.NamespaceURI);
269                                 Attr attr = table [attrName] as Attr;
270                                 if (attr == null) {
271                                         attList.Add (InferNewAttribute (
272                                                 attrName, isNew));
273                                 } else {
274                                         table.Remove (attrName);
275                                         if (attr.RefName != null &&
276                                                 attr.RefName != QName.Empty)
277                                                 continue; // just a reference
278                                         InferMergedAttribute (attr);
279                                 }
280                         } while (source.MoveToNextAttribute ());
281
282                         // mark all attr definitions that did not appear
283                         // as optional.
284                         if (table != null)
285                                 foreach (Attr attr in table.Values)
286                                         attr.Use = Use.Optional;
287                 }
288
289                 private XmlSchemaAttribute InferNewAttribute (
290                         QName attrName, bool isNewTypeDefinition)
291                 {
292                         Attr attr = null;
293                         bool mergedRequired = false;
294                         if (attrName.Namespace.Length > 0) {
295                                 // global attribute; might be already defined.
296                                 attr = GetGlobalAttribute (attrName) as Attr;
297                                 if (attr == null) {
298                                         attr = CreateGlobalAttribute (attrName);
299                                         attr.SchemaTypeName =
300                                                 InferSimpleType (source.Value);
301                                 } else {
302                                         InferMergedAttribute (attr);
303                                         mergedRequired =
304                                                 attr.Use == Use.Required;
305                                 }
306                                 attr = new Attr ();
307                                 attr.RefName = attrName;
308                         } else {
309                                 // local attribute
310                                 attr = new Attr ();
311                                 attr.Name = attrName.Name;
312                                 attr.SchemaTypeName =
313                                         InferSimpleType (source.Value);
314                         }
315                         if (!laxOccurence &&
316                                 (isNewTypeDefinition || mergedRequired))
317                                 attr.Use = Use.Required;
318                         else
319                                 attr.Use = Use.Optional;
320
321                         return attr;
322                 }
323
324                 // validate string value agains attr and 
325                 // if invalid, then relax the type.
326                 private void InferMergedAttribute (Attr attr)
327                 {
328                         attr.SchemaTypeName = InferMergedType (source.Value,
329                                 attr.SchemaTypeName);
330                         attr.SchemaType = null;
331                 }
332
333                 private QName InferMergedType (string value, QName typeName)
334                 {
335                         // examine value against specified type and
336                         // if unacceptable, then return a relaxed type.
337
338                         SimpleType st = XmlSchemaType.GetBuiltInSimpleType (
339                                 typeName);
340                         if (st == null) // non-primitive type => see above.
341                                 return QNameString;
342                         try {
343                                 st.Datatype.ParseValue (value,
344                                         source.NameTable,
345                                         source as IXmlNamespaceResolver);
346                                 // the string value was value
347                                 return typeName;
348                         } catch {
349                                 // The types were incompatible.
350                                 // FIXME: find the base common type
351                                 return QNameString;
352                         }
353                 }
354
355                 private SOMList GetAttributes (ComplexType ct)
356                 {
357                         if (ct.ContentModel == null)
358                                 return ct.Attributes;
359
360                         SimpleModel sc = ct.ContentModel as SimpleModel;
361                         if (sc != null) {
362                                 SimpleExt sce = sc.Content as SimpleExt;
363                                 if (sce != null)
364                                         return sce.Attributes;
365                                 SimpleRst scr = sc.Content as SimpleRst;
366                                 if (scr != null)
367                                         return scr.Attributes;
368                                 else
369                                         throw Error (sc, "Invalid simple content model.");
370                         }
371                         ComplexModel cc = ct.ContentModel as ComplexModel;
372                         if (cc != null) {
373                                 ComplexExt cce = cc.Content as ComplexExt;
374                                 if (cce != null)
375                                         return cce.Attributes;
376                                 ComplexRst ccr = cc.Content as ComplexRst;
377                                 if (ccr != null)
378                                         return ccr.Attributes;
379                                 else
380                                         throw Error (cc, "Invalid simple content model.");
381                         }
382                         throw Error (cc, "Invalid complexType. Should not happen.");
383                 }
384
385                 private ComplexType ToComplexType (Element el)
386                 {
387                         QName name = el.SchemaTypeName;
388                         XmlSchemaType type = el.SchemaType;
389
390                         // 1. element type is complex.
391                         ComplexType ct = type as ComplexType;
392                         if (ct != null)
393                                 return ct;
394
395                         // 2. reference to global complexType.
396                         XmlSchemaType globalType = schemas.GlobalTypes [name]
397                                 as XmlSchemaType;
398                         ct = globalType as ComplexType;
399                         if (ct != null)
400                                 return ct;
401
402                         ct = new ComplexType ();
403                         el.SchemaType = ct;
404                         el.SchemaTypeName = QName.Empty;
405
406                         // 3. base type name is xs:anyType or no specification.
407                         // <xs:complexType />
408                         if (name == QNameAnyType)
409                                 return ct;
410                         else if (type == null && name == QName.Empty)
411                                 return ct;
412
413                         SimpleModel sc = new SimpleModel ();
414                         ct.ContentModel = sc;
415
416                         // 4. type is simpleType
417                         //    -> extension of existing simple type.
418                         SimpleType st = type as SimpleType;
419                         if (st != null) {
420                                 SimpleRst scr = new SimpleRst ();
421                                 scr.BaseType = st;
422                                 sc.Content = scr;
423                                 return ct;
424                         }
425
426                         SimpleExt sce = new SimpleExt ();
427                         sc.Content = sce;
428
429                         // 5. type name points to primitive type
430                         //    -> simple extension of a primitive type
431                         st = XmlSchemaType.GetBuiltInSimpleType (name);
432                         if (st != null) {
433                                 sce.BaseTypeName = name;
434                                 return ct;
435                         }
436
437                         // 6. type name points to global simpleType.
438                         st = globalType as SimpleType;
439                         if (st != null) {
440                                 sce.BaseTypeName = name;
441                                 return ct;
442                         }
443
444                         throw Error (el, "Unexpected schema component that contains simpleTypeName that could not be resolved.");
445                 }
446
447                 #endregion
448
449                 #region Element Type
450
451                 private void InferAsEmptyElement (Element el, string ns,
452                         bool isNew)
453                 {
454                         ComplexType ct = el.SchemaType as ComplexType;
455                         if (ct != null) {
456                                 SimpleModel sm =
457                                         ct.ContentModel as SimpleModel;
458                                 if (sm != null) {
459                                         ToEmptiableSimpleContent (sm, isNew);
460                                         return;
461                                 }
462
463                                 ComplexModel cm = ct.ContentModel
464                                         as ComplexModel;
465                                 if (cm != null) {
466                                         ToEmptiableComplexContent (cm, isNew);
467                                         return;
468                                 }
469
470                                 if (ct.Particle != null)
471                                         ct.Particle.MinOccurs = 0;
472                                 return;
473                         }
474                         SimpleType st = el.SchemaType as SimpleType;
475                         if (st != null) {
476                                 st = MakeBaseTypeAsEmptiable (st);
477                                 switch (st.QualifiedName.Namespace) {
478                                 case XmlSchema.Namespace:
479                                 case XdtNamespace:
480                                         el.SchemaTypeName = st.QualifiedName;
481                                         break;
482                                 default:
483                                         el.SchemaType =st;
484                                         break;
485                                 }
486                         }
487                 }
488
489                 private SimpleType MakeBaseTypeAsEmptiable (SimpleType st)
490                 {
491                         switch (st.QualifiedName.Namespace) {
492                         case XmlSchema.Namespace:
493                         case XdtNamespace:
494                                 // If a primitive type
495                                 return XmlSchemaType.GetBuiltInSimpleType (
496                                         XmlTypeCode.String);
497                         }
498                         SimpleTypeRst str = st.Content as SimpleTypeRst;
499                         if (str != null) {
500                                 ArrayList al = null;
501                                 foreach (SchemaFacet f in str.Facets) {
502                                         if (f is LengthFacet ||
503                                                 f is MinLengthFacet) {
504                                                 if (al == null)
505                                                         al = new ArrayList ();
506                                                 al.Add (f);
507                                         }
508                                 }
509                                 foreach (SchemaFacet f in al)
510                                         str.Facets.Remove (f);
511                                 if (str.BaseType != null)
512                                         str.BaseType =
513                                                 MakeBaseTypeAsEmptiable (st);
514                                 else
515                                         // It might have a reference to an
516                                         // external simple type, but there is
517                                         // no assurance that any of those
518                                         // external types allow an empty
519                                         // string. So just set base type as
520                                         // xs:string.
521                                         str.BaseTypeName = QNameString;
522                         } // union/list can have empty string value.
523
524                         return st;
525                 }
526
527                 private void ToEmptiableSimpleContent (
528                         SimpleModel sm, bool isNew)
529                 {
530                         SimpleExt se = sm.Content as SimpleExt;
531                         if (se != null)
532                                 se.BaseTypeName = QNameString;
533                         else {
534                                 SimpleRst sr = sm.Content
535                                         as SimpleRst;
536                                 if (sr == null)
537                                         throw Error (sm, "Invalid simple content model was passed.");
538                                 sr.BaseTypeName = QNameString;
539                                 sr.BaseType = null;
540                         }
541                 }
542
543                 private void ToEmptiableComplexContent (
544                         ComplexModel cm, bool isNew)
545                 {
546                         ComplexExt ce = cm.Content
547                                 as ComplexExt;
548                         if (ce != null) {
549                                 if (ce.Particle != null)
550                                         ce.Particle.MinOccurs = 0;
551                                 else if (ce.BaseTypeName != null &&
552                                         ce.BaseTypeName != QName.Empty &&
553                                         ce.BaseTypeName != QNameAnyType)
554                                         throw Error (ce, "Complex type content extension has a reference to an external component that is not supported.");
555                         }
556                         else {
557                                 ComplexRst cr = cm.Content
558                                         as ComplexRst;
559                                 if (cr == null)
560                                         throw Error (cm, "Invalid complex content model was passed.");
561                                 if (cr.Particle != null)
562                                         cr.Particle.MinOccurs = 0;
563                                 else if (cr.BaseTypeName != null &&
564                                         cr.BaseTypeName != QName.Empty &&
565                                         cr.BaseTypeName != QNameAnyType)
566                                         throw Error (cr, "Complex type content extension has a reference to an external component that is not supported.");
567                         }
568                 }
569
570                 private void InferContent (Element el, string ns, bool isNew)
571                 {
572                         source.Read ();
573                         source.MoveToContent ();
574                         switch (source.NodeType) {
575                         case XmlNodeType.EndElement:
576                                 InferAsEmptyElement (el, ns, isNew);
577                                 break;
578                         case XmlNodeType.Element:
579                                 InferComplexContent (el, ns, isNew);
580                                 break;
581                         case XmlNodeType.Text:
582                         case XmlNodeType.CDATA:
583                         case XmlNodeType.SignificantWhitespace:
584                                 InferTextContent (el, isNew);
585                                 source.MoveToContent ();
586                                 if (source.NodeType == XmlNodeType.Element)
587                                         goto case XmlNodeType.Element;
588                                 break;
589                         case XmlNodeType.Whitespace:
590                                 InferContent (el, ns, isNew); // skip and retry
591                                 break;
592                         }
593                 }
594
595                 private void InferComplexContent (Element el, string ns,
596                         bool isNew)
597                 {
598                         ComplexType ct = ToComplexType (el);
599                         ToComplexContentType (ct);
600
601                         int position = 0;
602                         bool consumed = false;
603
604                         do {
605                                 switch (source.NodeType) {
606                                 case XmlNodeType.Element:
607                                         Sequence s = PopulateSequence (ct);
608                                         Choice c = s.Items.Count > 0 ?
609                                                 s.Items [0] as Choice :
610                                                 null;
611                                         if (c != null)
612                                                 ProcessLax (c, ns);
613                                         else
614                                                 ProcessSequence (ct, s, ns,
615                                                         ref position,
616                                                         ref consumed,
617                                                         isNew);
618                                         source.MoveToContent ();
619                                         break;
620                                 case XmlNodeType.Text:
621                                 case XmlNodeType.CDATA:
622                                 case XmlNodeType.SignificantWhitespace:
623                                         MarkAsMixed (ct);
624                                         source.ReadString ();
625                                         source.MoveToContent ();
626                                         break;
627                                 case XmlNodeType.EndElement:
628                                         return; // finished
629                                 case XmlNodeType.None:
630                                         throw new NotImplementedException ("Internal Error: Should not happen.");
631                                 }
632                         } while (true);
633                 }
634
635                 private void InferTextContent (Element el, bool isNew)
636                 {
637                         string value = source.ReadString ();
638                         if (el.SchemaType == null) {
639                                 if (el.SchemaTypeName == QName.Empty) {
640                                         // no type information -> infer type
641                                         if (isNew)
642                                                 el.SchemaTypeName =
643                                                         InferSimpleType (
644                                                         value);
645                                         else
646                                                 el.SchemaTypeName =
647                                                         QNameString;
648                                         return;
649                                 }
650                                 switch (el.SchemaTypeName.Namespace) {
651                                 case XmlSchema.Namespace:
652                                 case XdtNamespace:
653                                         // existing primitive type
654                                         el.SchemaTypeName = InferMergedType (
655                                                 value, el.SchemaTypeName);
656                                         break;
657                                 default:
658                                         ComplexType ct = schemas.GlobalTypes [
659                                                 el.SchemaTypeName]
660                                                 as ComplexType;
661                                         // If it is complex, then just set
662                                         // mixed='true' (type cannot be set.)
663                                         // If it is simple, then we cannot
664                                         // make sure that string value is
665                                         // valid. So just set as xs:string.
666                                         if (ct != null)
667                                                 MarkAsMixed (ct);
668                                         else
669                                                 el.SchemaTypeName = QNameString;
670                                         break;
671                                 }
672                                 return;
673                         }
674                         // simpleType
675                         SimpleType st = el.SchemaType as SimpleType;
676                         if (st != null) {
677                                 // If simple, then (described above)
678                                 el.SchemaType = null;
679                                 el.SchemaTypeName = QNameString;
680                                 return;
681                         }
682
683                         // complexType
684                         ComplexType ect = el.SchemaType as ComplexType;
685
686                         SimpleModel sm = ect.ContentModel as SimpleModel;
687                         if (sm == null) {
688                                 // - ComplexContent
689                                 MarkAsMixed (ect);
690                                 return;
691                         }
692
693                         // - SimpleContent
694                         SimpleExt se = sm.Content as SimpleExt;
695                         if (se != null)
696                                 se.BaseTypeName = InferMergedType (value,
697                                         se.BaseTypeName);
698                         SimpleRst sr = sm.Content as SimpleRst;
699                         if (sr != null) {
700                                 sr.BaseTypeName = InferMergedType (value,
701                                         sr.BaseTypeName);
702                                 sr.BaseType = null;
703                         }
704                 }
705
706                 private void MarkAsMixed (ComplexType ct)
707                 {
708                         ComplexModel cm = ct.ContentModel as ComplexModel;
709                         if (cm != null)
710                                 cm.IsMixed = true;
711                         else
712                                 ct.IsMixed = true;
713                 }
714
715                 #endregion
716
717                 #region Particles
718
719                 private void ProcessLax (Choice c, string ns)
720                 {
721                         foreach (Particle p in c.Items) {
722                                 Element el = p as Element;
723                                 if (el == null)
724                                         throw Error (c, String.Format ("Target schema item contains unacceptable particle {0}. Only element is allowed here."));
725                                 if (ElementMatches (el, ns)) {
726                                         InferElement (el, ns, false);
727                                         return;
728                                 }
729                         }
730                         // append a new element particle to lax term.
731                         Element nel = new Element ();
732                         if (source.NamespaceURI == ns)
733                                 nel.Name = source.LocalName;
734                         else
735                                 nel.RefName = new QName (source.LocalName,
736                                         source.NamespaceURI);
737                         InferElement (nel, source.NamespaceURI, true);
738                         c.Items.Add (nel);
739                 }
740
741                 private bool ElementMatches (Element el, string ns)
742                 {
743                         bool matches = false;
744                         if (el.RefName != QName.Empty) {
745                                 if (el.RefName.Name == source.LocalName &&
746                                         el.RefName.Namespace ==
747                                         source.NamespaceURI)
748                                         matches = true;
749                         }
750                         else if (el.Name == source.LocalName &&
751                                 ns == source.NamespaceURI)
752                                         matches = true;
753                         return matches;
754                 }
755
756                 private void ProcessSequence (ComplexType ct, Sequence s,
757                         string ns, ref int position, ref bool consumed,
758                         bool isNew)
759                 {
760                         for (int i = 0; i < position; i++) {
761                                 Element iel = s.Items [i] as Element;
762                                 if (ElementMatches (iel, ns)) {
763                                         // Sequence element type violation
764                                         // might happen (might not, but we
765                                         // cannot backtrack here). So switch
766                                         // to sequence of choice* here.
767                                         ProcessLax (ToSequenceOfChoice (s), ns);
768                                         return;
769                                 }
770                         }
771
772                         if (s.Items.Count <= position) {
773                                 QName name = new QName (source.LocalName,
774                                         source.NamespaceURI);
775                                 Element nel = CreateElement (name);
776                                 InferElement (nel, ns, true);
777                                 if (ns == name.Namespace)
778                                         s.Items.Add (nel);
779                                 else {
780                                         Element re = new Element ();
781                                         re.RefName = name;
782                                         s.Items.Add (re);
783                                 }
784                                 consumed = true;
785                                 return;
786                         }
787                         Element el = s.Items [position] as Element;
788                         if (el == null)
789                                 throw Error (s, String.Format ("Target complex type content sequence has an unacceptable type of particle {0}", s.Items [position]));
790                         bool matches = ElementMatches (el, ns);
791                         if (matches) {
792                                 if (consumed)
793                                         el.MaxOccursString = "unbounded";
794                                 InferElement (el, source.NamespaceURI, false);
795                                 source.MoveToContent ();
796                                 switch (source.NodeType) {
797                                 case XmlNodeType.None:
798                                         if (source.NodeType ==
799                                                 XmlNodeType.Element)
800                                                 goto case XmlNodeType.Element;
801                                         else if (source.NodeType ==
802                                                 XmlNodeType.EndElement)
803                                                 goto case XmlNodeType.EndElement;
804                                         break;
805                                 case XmlNodeType.Element:
806                                         ProcessSequence (ct, s, ns, ref position,
807                                                 ref consumed, isNew);
808                                         break;
809                                 case XmlNodeType.Text:
810                                 case XmlNodeType.CDATA:
811                                 case XmlNodeType.SignificantWhitespace:
812                                         MarkAsMixed (ct);
813                                         source.ReadString ();
814                                         goto case XmlNodeType.None;
815                                 case XmlNodeType.Whitespace:
816                                         source.ReadString ();
817                                         goto case XmlNodeType.None;
818                                 case XmlNodeType.EndElement:
819                                         return;
820                                 default:
821                                         source.Read ();
822                                         break;
823                                 }
824                         }
825                         else {
826                                 if (consumed) {
827                                         position++;
828                                         consumed = false;
829                                         ProcessSequence (ct, s, ns,
830                                                 ref position, ref consumed,
831                                                 isNew);
832                                 }
833                                 else
834                                         ProcessLax (ToSequenceOfChoice (s), ns);
835                         }
836                 }
837
838                 // Note that it does not return the changed sequence.
839                 private Choice ToSequenceOfChoice (Sequence s)
840                 {
841                         Choice c = new Choice ();
842                         if (laxOccurence)
843                                 c.MinOccurs = 0;
844                         c.MaxOccursString = "unbounded";
845                         foreach (Particle p in s.Items)
846                                 c.Items.Add (p);
847                         s.Items.Clear ();
848                         s.Items.Add (c);
849                         return c;
850                 }
851
852                 // It makes complexType not to have Simple content model.
853                 private void ToComplexContentType (ComplexType type)
854                 {
855                         SimpleModel sm = type.ContentModel as SimpleModel;
856                         if (sm == null)
857                                 return;
858
859                         SOMList atts = GetAttributes (type);
860                         foreach (SOMObject o in atts)
861                                 type.Attributes.Add (o);
862                         // FIXME: need to copy AnyAttribute.
863                         // (though not considered right now)
864                         type.ContentModel = null;
865                         type.IsMixed = true;
866                 }
867
868                 private Sequence PopulateSequence (ComplexType ct)
869                 {
870                         Particle p = PopulateParticle (ct);
871                         Sequence s = p as Sequence;
872                         if (s != null)
873                                 return s;
874                         else
875                                 throw Error (ct, String.Format ("Target complexType contains unacceptable type of particle {0}", p));
876                 }
877
878                 private Sequence CreateSequence ()
879                 {
880                         Sequence s = new Sequence ();
881                         if (laxOccurence)
882                                 s.MinOccurs = 0;
883                         return s;
884                 }
885
886                 private Particle PopulateParticle (ComplexType ct)
887                 {
888                         if (ct.ContentModel == null) {
889                                 if (ct.Particle == null)
890                                         ct.Particle = CreateSequence ();
891                                 return ct.Particle;
892                         }
893                         ComplexModel cm = ct.ContentModel as ComplexModel;
894                         if (cm != null) {
895                                 ComplexExt  ce = cm.Content as ComplexExt;
896                                 if (ce != null) {
897                                         if (ce.Particle == null)
898                                                 ce.Particle = CreateSequence ();
899                                         return ce.Particle;
900                                 }
901                                 ComplexRst cr = cm.Content as ComplexRst;
902                                 if (cr != null) {
903                                         if (cr.Particle == null)
904                                                 cr.Particle = CreateSequence ();
905                                         return cr.Particle;
906                                 }
907                         }
908                         throw Error (ct, "Schema inference internal error. The complexType should have been converted to have a complex content.");
909                 }
910
911                 #endregion
912
913                 #region String Value
914
915                 // primitive type inference.
916                 // When running lax type inference, it just returns xs:string.
917                 private QName InferSimpleType (string value)
918                 {
919                         if (laxTypeInference)
920                                 return QNameString;
921
922                         switch (value) {
923                         // 0 and 1 are not infered as byte unlike MS.XSDInfer
924 //                      case "0":
925 //                      case "1":
926                         case "true":
927                         case "false":
928                                 return QNameBoolean;
929                         }
930                         try {
931                                 long dec = XmlConvert.ToInt64 (value);
932                                 if (byte.MinValue <= dec && dec <= byte.MaxValue)
933                                         return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.UnsignedByte).QualifiedName;
934                                 if (sbyte.MinValue <= dec && dec <= sbyte.MaxValue)
935                                         return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Byte).QualifiedName;
936                                 if (ushort.MinValue <= dec && dec <= ushort.MaxValue)
937                                         return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.UnsignedShort).QualifiedName;
938                                 if (short.MinValue <= dec && dec <= short.MaxValue)
939                                         return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Short).QualifiedName;
940                                 if (uint.MinValue <= dec && dec <= uint.MaxValue)
941                                         return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.UnsignedInt).QualifiedName;
942                                 if (int.MinValue <= dec && dec <= int.MaxValue)
943                                         return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Int).QualifiedName;
944                                 return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Long).QualifiedName;
945                         } catch (Exception) {
946                         }
947                         try {
948                                 XmlConvert.ToUInt64 (value);
949                                 return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.UnsignedLong).QualifiedName;
950                         } catch (Exception) {
951                         }
952                         try {
953                                 XmlConvert.ToDecimal (value);
954                                 return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Decimal).QualifiedName;
955                         } catch (Exception) {
956                         }
957                         try {
958                                 double dbl = XmlConvert.ToDouble (value);
959                                 if (float.MinValue <= dbl &&
960                                         dbl <= float.MaxValue)
961                                         return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Float).QualifiedName;
962                                 else
963                                         return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Double).QualifiedName;
964                         } catch (Exception) {
965                         }
966                         try {
967                                 // FIXME: also try DateTimeSerializationMode
968                                 // and gYearMonth
969                                 XmlConvert.ToDateTime (value);
970                                 return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.DateTime).QualifiedName;
971                         } catch (Exception) {
972                         }
973                         try {
974                                 XmlConvert.ToTimeSpan (value);
975                                 return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Duration).QualifiedName;
976                         } catch (Exception) {
977                         }
978
979                         // xs:string
980                         return QNameString;
981                 }
982
983                 #endregion
984
985                 #region Utilities
986
987                 private Element GetGlobalElement (QName name)
988                 {
989                         Element el = newElements [name] as Element;
990                         if (el == null)
991                                 el = schemas.GlobalElements [name] as Element;
992                         return el;
993                 }
994
995                 private Attr GetGlobalAttribute (QName name)
996                 {
997                         Attr a = newElements [name] as Attr;
998                         if (a == null)
999                                 a = schemas.GlobalAttributes [name] as Attr;
1000                         return a;
1001                 }
1002
1003                 private Element CreateElement (QName name)
1004                 {
1005                         Element el = new Element ();
1006                         el.Name = name.Name;
1007                         return el;
1008                 }
1009
1010                 private Element CreateGlobalElement (QName name)
1011                 {
1012                         Element el = CreateElement (name);
1013                         XmlSchema schema = PopulateSchema (name.Namespace);
1014                         schema.Items.Add (el);
1015                         newElements.Add (name, el);
1016                         return el;
1017                 }
1018
1019                 private Attr CreateGlobalAttribute (QName name)
1020                 {
1021                         Attr attr = new Attr ();
1022                         XmlSchema schema = PopulateSchema (name.Namespace);
1023                         attr.Name = name.Name;
1024                         schema.Items.Add (attr);
1025                         newAttributes.Add (name, attr);
1026                         return attr;
1027                 }
1028
1029                 // Note that the return value never assures that all the
1030                 // components in the parameter ns must reside in it.
1031                 private XmlSchema PopulateSchema (string ns)
1032                 {
1033                         ICollection list = schemas.Schemas (ns);
1034                         if (list.Count > 0) {
1035                                 IEnumerator e = list.GetEnumerator ();
1036                                 e.MoveNext ();
1037                                 return (XmlSchema) e.Current;
1038                         }
1039                         XmlSchema s = new XmlSchema ();
1040                         if (ns != null && ns.Length > 0)
1041                                 s.TargetNamespace = ns;
1042                         s.ElementFormDefault = Form.Qualified;
1043                         s.AttributeFormDefault = Form.Unqualified;
1044                         schemas.Add (s);
1045                         return s;
1046                 }
1047
1048                 private XmlSchemaInferenceException Error (
1049                         XmlSchemaObject sourceObj,
1050                         string message)
1051                 {
1052                         // This override is mainly for schema component error.
1053                         return Error (sourceObj, false, message);
1054                 }
1055
1056                 private XmlSchemaInferenceException Error (
1057                         XmlSchemaObject sourceObj,
1058                         bool useReader,
1059                         string message)
1060                 {
1061                         string msg = String.Concat (
1062                                 message,
1063                                 sourceObj != null ?
1064                                         String.Format (". Related schema component is {0}",
1065                                                 sourceObj.SourceUri,
1066                                                 sourceObj.LineNumber,
1067                                                 sourceObj.LinePosition) :
1068                                         String.Empty,
1069                                 useReader ?
1070                                         String.Format (". {0}", source.BaseURI) :
1071                                         String.Empty);
1072
1073                         IXmlLineInfo li = source as IXmlLineInfo;
1074                         if (useReader && li != null)
1075                                 return new XmlSchemaInferenceException (
1076                                         msg, null, li.LineNumber,
1077                                         li.LinePosition);
1078                         else
1079                                 return new XmlSchemaInferenceException (msg);
1080                 }
1081
1082                 #endregion
1083         }
1084 }
1085
1086 #endif