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