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