2004-12-11 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / Mono.Xml.Schema / XsdValidatingReader.cs
1 //
2 // Mono.Xml.Schema.XsdValidatingReader.cs
3 //
4 // Author:
5 //      Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 //      (C)2003 Atsushi Enomoto
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 using System;
31 using System.Collections;
32 using System.Collections.Specialized;
33 using System.IO;
34 using System.Text;
35 using System.Xml;
36 using System.Xml.Schema;
37 using Mono.Xml;
38
39 using ContentProc = System.Xml.Schema.XmlSchemaContentProcessing;
40 using XsElement = System.Xml.Schema.XmlSchemaElement;
41 using XsAttribute = System.Xml.Schema.XmlSchemaAttribute;
42 using ComplexType = System.Xml.Schema.XmlSchemaComplexType;
43 using SimpleType = System.Xml.Schema.XmlSchemaSimpleType;
44 using SimpleTypeRest = System.Xml.Schema.XmlSchemaSimpleTypeRestriction;
45 using SimpleTypeList = System.Xml.Schema.XmlSchemaSimpleTypeList;
46 using SimpleTypeUnion = System.Xml.Schema.XmlSchemaSimpleTypeUnion;
47 using XsDatatype = System.Xml.Schema.XmlSchemaDatatype;
48
49 namespace Mono.Xml.Schema
50 {
51         internal class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext, IXmlNamespaceResolver
52         {
53                 static readonly XsAttribute [] emptyAttributeArray =
54                         new XsAttribute [0];
55
56                 XmlReader reader;
57                 XmlResolver resolver;
58                 IHasXmlSchemaInfo sourceReaderSchemaInfo;
59                 IXmlLineInfo readerLineInfo;
60                 ValidationType validationType;
61                 XmlSchemaSet schemas = new XmlSchemaSet ();
62                 bool namespaces = true;
63
64 #region ID Constraints
65                 bool checkIdentity = true;
66                 Hashtable idList = new Hashtable ();
67                 ArrayList missingIDReferences;
68                 string thisElementId;
69 #endregion
70
71 #region Key Constraints
72                 bool checkKeyConstraints = true;
73                 ArrayList keyTables = new ArrayList ();
74                 ArrayList currentKeyFieldConsumers;
75                 ArrayList tmpKeyrefPool;
76                 ArrayList elementQNameStack = new ArrayList ();
77 #endregion
78
79                 XsdParticleStateManager state = new XsdParticleStateManager ();
80
81                 int skipValidationDepth = -1;
82                 int xsiNilDepth = -1;
83                 StringBuilder storedCharacters = new StringBuilder ();
84                 bool shouldValidateCharacters;
85
86                 XsAttribute [] defaultAttributes = emptyAttributeArray;
87                 int currentDefaultAttribute = -1;
88                 ArrayList defaultAttributesCache = new ArrayList ();
89                 bool defaultAttributeConsumed;
90                 object currentAttrType;
91
92 #region .ctor
93                 public XsdValidatingReader (XmlReader reader)
94                 {
95                         this.reader = reader;
96                         readerLineInfo = reader as IXmlLineInfo;
97                         sourceReaderSchemaInfo = reader as IHasXmlSchemaInfo;
98                         schemas.ValidationEventHandler += ValidationEventHandler;
99                 }
100 #endregion
101
102                 public ValidationEventHandler ValidationEventHandler;
103
104                 // Private Properties
105
106                 private XsdValidationContext Context {
107                         get { return state.Context; }
108                 }
109
110 #region Key Constraints
111                 internal ArrayList CurrentKeyFieldConsumers {
112                         get {
113                                 if (currentKeyFieldConsumers == null)
114                                         currentKeyFieldConsumers = new ArrayList ();
115                                 return currentKeyFieldConsumers;
116                         }
117                 }
118 #endregion
119
120                 private ArrayList MissingIDReferences {
121                         get {
122                                 if (missingIDReferences == null)
123                                         missingIDReferences = new ArrayList ();
124                                 return missingIDReferences;
125                         }
126                 }
127
128                 // Public Non-overrides
129
130                 public int XsiNilDepth {
131                         get { return xsiNilDepth; }
132                 }
133
134                 public bool Namespaces {
135                         get { return namespaces; }
136                         set { namespaces = value; }
137                 }
138
139                 // This is required to resolve xsi:schemaLocation
140                 public XmlResolver XmlResolver {
141                         set {
142                                 resolver = value;
143                         }
144                 }
145
146                 // This should be changed before the first Read() call.
147                 public XmlSchemaSet Schemas {
148                         get { return schemas; }
149                         set {
150                                 if (ReadState != ReadState.Initial)
151                                         throw new InvalidOperationException ("Schemas must be set before the first call to Read().");
152                                 schemas = value;
153                         }
154                 }
155
156                 public object SchemaType {
157                         get {
158                                 if (ReadState != ReadState.Interactive)
159                                         return null;
160
161                                 switch (NodeType) {
162                                 case XmlNodeType.Element:
163                                         if (Context.ActualType != null)
164                                                 return Context.ActualType;
165                                         else
166                                                 return SourceReaderSchemaType;
167                                 case XmlNodeType.Attribute:
168                                         if (currentAttrType == null) {
169                                                 ComplexType ct = Context.ActualType as ComplexType;
170                                                 if (ct != null) {
171                                                         XsAttribute attdef = ct.AttributeUses [new XmlQualifiedName (LocalName, NamespaceURI)] as XsAttribute;
172                                                         if (attdef != null)
173                                                                 currentAttrType = attdef.AttributeType;
174                                                         return currentAttrType;
175                                                 }
176                                                 currentAttrType = SourceReaderSchemaType;
177                                         }
178                                         return currentAttrType;
179                                 default:
180                                         return SourceReaderSchemaType;
181                                 }
182                         }
183                 }
184
185                 private object SourceReaderSchemaType {
186                         get { return this.sourceReaderSchemaInfo != null ? sourceReaderSchemaInfo.SchemaType : null; }
187                 }
188
189                 public ValidationType ValidationType {
190                         get { return validationType; }
191                         set {
192                                 if (ReadState != ReadState.Initial)
193                                         throw new InvalidOperationException ("ValidationType must be set before reading.");
194                                 validationType = value;
195                         }
196                 }
197
198                 IDictionary IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
199                 {
200                         IXmlNamespaceResolver resolver = reader as IXmlNamespaceResolver;
201                         if (resolver == null)
202                                 throw new NotSupportedException ("The input XmlReader does not implement IXmlNamespaceResolver and thus this validating reader cannot collect in-scope namespaces.");
203                         return resolver.GetNamespacesInScope (scope);
204                 }
205
206                 string IXmlNamespaceResolver.LookupPrefix (string ns)
207                 {
208                         return ((IXmlNamespaceResolver) this).LookupPrefix (ns, false);
209                 }
210
211                 string IXmlNamespaceResolver.LookupPrefix (string ns, bool atomizedNames)
212                 {
213                         IXmlNamespaceResolver resolver = reader as IXmlNamespaceResolver;
214                         if (resolver == null)
215                                 throw new NotSupportedException ("The input XmlReader does not implement IXmlNamespaceResolver and thus this validating reader cannot execute namespace prefix lookup.");
216                         return resolver.LookupPrefix (ns, atomizedNames);
217                 }
218
219                 // It is used only for independent XmlReader use, not for XmlValidatingReader.
220 #if NET_2_0
221                 public override object ReadTypedValue ()
222 #else
223                 public object ReadTypedValue ()
224 #endif
225                 {
226                         object o = XmlSchemaUtil.ReadTypedValue (this,
227                                 SchemaType, ParserContext.NamespaceManager,
228                                 storedCharacters);
229                         storedCharacters.Length = 0;
230                         return o;
231                 }
232                 
233                 private object ReadTypedValueCore ()
234                 {
235                         XsDatatype dt = SchemaType as XsDatatype;
236                         SimpleType st = SchemaType as SimpleType;
237                         if (st != null)
238                                 dt = st.Datatype;
239                         if (dt == null)
240                                 return null;
241
242                         switch (NodeType) {
243                         case XmlNodeType.Element:
244                                 if (IsEmptyElement)
245                                         return null;
246
247                                 storedCharacters.Length = 0;
248                                 bool loop = true;
249                                 do {
250                                         Read ();
251                                         switch (NodeType) {
252                                         case XmlNodeType.SignificantWhitespace:
253                                         case XmlNodeType.Text:
254                                         case XmlNodeType.CDATA:
255                                                 storedCharacters.Append (Value);
256                                                 break;
257                                         case XmlNodeType.Comment:
258                                                 break;
259                                         default:
260                                                 loop = false;
261                                                 break;
262                                         }
263                                 } while (loop && !EOF && ReadState == ReadState.Interactive);
264                                 return dt.ParseValue (storedCharacters.ToString (), NameTable, ParserContext.NamespaceManager);
265                         case XmlNodeType.Attribute:
266                                 return dt.ParseValue (Value, NameTable, ParserContext.NamespaceManager);
267                         }
268                         return null;
269                 }
270
271                 // Public Overriden Properties
272
273                 public override int AttributeCount {
274                         get {
275                                 return reader.AttributeCount + defaultAttributes.Length;
276                         }
277                 }
278
279                 public override string BaseURI {
280                         get { return reader.BaseURI; }
281                 }
282
283                 // If this class is used to implement XmlValidatingReader,
284                 // it should be left to DTDValidatingReader. In other cases,
285                 // it depends on the reader's ability.
286                 public override bool CanResolveEntity {
287                         get { return reader.CanResolveEntity; }
288                 }
289
290                 public override int Depth {
291                         get {
292                                 if (currentDefaultAttribute < 0)
293                                         return reader.Depth;
294                                 if (this.defaultAttributeConsumed)
295                                         return reader.Depth + 2;
296                                 return reader.Depth + 1;
297                         }
298                 }
299
300                 public override bool EOF {
301                         get { return reader.EOF; }
302                 }
303
304                 public override bool HasValue {
305                         get {
306                                 if (currentDefaultAttribute < 0)
307                                         return reader.HasValue;
308                                 return true;
309                         }
310                 }
311
312                 public override bool IsDefault {
313                         get {
314                                 if (currentDefaultAttribute < 0)
315                                         return reader.IsDefault;
316                                 return true;
317                         }
318                 }
319
320                 public override bool IsEmptyElement {
321                         get {
322                                 if (currentDefaultAttribute < 0)
323                                         return reader.IsEmptyElement;
324                                 return false;
325                         }
326                 }
327
328                 public override string this [int i] {
329                         get { return GetAttribute (i); }
330                 }
331
332                 public override string this [string name] {
333                         get { return GetAttribute (name); }
334                 }
335
336                 public override string this [string localName, string ns] {
337                         get { return GetAttribute (localName, ns); }
338                 }
339
340                 int IXmlLineInfo.LineNumber {
341                         get { return readerLineInfo != null ? readerLineInfo.LineNumber : 0; }
342                 }
343
344                 int IXmlLineInfo.LinePosition {
345                         get { return readerLineInfo != null ? readerLineInfo.LinePosition : 0; }
346                 }
347
348                 public override string LocalName {
349                         get {
350                                 if (currentDefaultAttribute < 0)
351                                         return reader.LocalName;
352                                 if (defaultAttributeConsumed)
353                                         return String.Empty;
354                                 return defaultAttributes [currentDefaultAttribute].QualifiedName.Name;
355                         }
356                 }
357
358                 public override string Name {
359                         get {
360                                 if (currentDefaultAttribute < 0)
361                                         return reader.Name;
362                                 if (defaultAttributeConsumed)
363                                         return String.Empty;
364
365                                 XmlQualifiedName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
366                                 string prefix = Prefix;
367                                 if (prefix == String.Empty)
368                                         return qname.Name;
369                                 else
370                                         return String.Concat (prefix, ":", qname.Name);
371                         }
372                 }
373
374                 public override string NamespaceURI {
375                         get {
376                                 if (currentDefaultAttribute < 0)
377                                         return reader.NamespaceURI;
378                                 if (defaultAttributeConsumed)
379                                         return String.Empty;
380                                 return defaultAttributes [currentDefaultAttribute].QualifiedName.Namespace;
381                         }
382                 }
383
384                 public override XmlNameTable NameTable {
385                         get { return reader.NameTable; }
386                 }
387
388                 public override XmlNodeType NodeType {
389                         get {
390                                 if (currentDefaultAttribute < 0)
391                                         return reader.NodeType;
392                                 if (defaultAttributeConsumed)
393                                         return XmlNodeType.Text;
394                                 return XmlNodeType.Attribute;
395                         }
396                 }
397
398                 public XmlParserContext ParserContext {
399                         get { return XmlSchemaUtil.GetParserContext (reader); }
400                 }
401
402                 public override string Prefix {
403                         get {
404                                 if (currentDefaultAttribute < 0)
405                                         return reader.Prefix;
406                                 if (defaultAttributeConsumed)
407                                         return String.Empty;
408                                 XmlQualifiedName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
409                                 string prefix = this.ParserContext.NamespaceManager.LookupPrefix (qname.Namespace, false);
410                                 if (prefix == null)
411                                         return String.Empty;
412                                 else
413                                         return prefix;
414                         }
415                 }
416
417                 public override char QuoteChar {
418                         get { return reader.QuoteChar; }
419                 }
420
421                 public override ReadState ReadState {
422                         get { return reader.ReadState; }
423                 }
424
425                 public override string Value {
426                         get {
427                                 if (currentDefaultAttribute < 0)
428                                         return reader.Value;
429                                 string value = defaultAttributes [currentDefaultAttribute].ValidatedDefaultValue;
430                                 if (value == null)
431                                         value = defaultAttributes [currentDefaultAttribute].ValidatedFixedValue;
432                                 return value;
433                         }
434                 }
435
436                 public override string XmlLang {
437                         get {
438                                 string xmlLang = reader.XmlLang;
439                                 if (xmlLang != null)
440                                         return xmlLang;
441                                 int idx = this.FindDefaultAttribute ("lang", XmlNamespaceManager.XmlnsXml);
442                                 if (idx < 0)
443                                         return null;
444                                 xmlLang = defaultAttributes [idx].ValidatedDefaultValue;
445                                 if (xmlLang == null)
446                                         xmlLang = defaultAttributes [idx].ValidatedFixedValue;
447                                 return xmlLang;
448                         }
449                 }
450
451                 public override XmlSpace XmlSpace {
452                         get {
453                                 XmlSpace space = reader.XmlSpace;
454                                 if (space != XmlSpace.None)
455                                         return space;
456                                 int idx = this.FindDefaultAttribute ("space", XmlNamespaceManager.XmlnsXml);
457                                 if (idx < 0)
458                                         return XmlSpace.None;
459                                 string spaceSpec = defaultAttributes [idx].ValidatedDefaultValue;
460                                 if (spaceSpec == null)
461                                         spaceSpec = defaultAttributes [idx].ValidatedFixedValue;
462                                 return (XmlSpace) Enum.Parse (typeof (XmlSpace), spaceSpec, false);
463                         }
464                 }
465
466                 // Private Methods
467
468                 private void HandleError (string error)
469                 {
470                         HandleError (error, null);
471                 }
472
473                 private void HandleError (string error, Exception innerException)
474                 {
475                         HandleError (error, innerException, false);
476                 }
477
478                 private void HandleError (string error, Exception innerException, bool isWarning)
479                 {
480                         if (ValidationType == ValidationType.None)      // extra quick check
481                                 return;
482
483                         XmlSchemaException schemaException = new XmlSchemaException (error, 
484                                         this, this.BaseURI, null, innerException);
485                         HandleError (schemaException, isWarning);
486                 }
487
488                 private void HandleError (XmlSchemaException schemaException)
489                 {
490                         HandleError (schemaException, false);
491                 }
492
493                 private void HandleError (XmlSchemaException schemaException, bool isWarning)
494                 {
495                         if (ValidationType == ValidationType.None)
496                                 return;
497
498                         ValidationEventArgs e = new ValidationEventArgs (schemaException,
499                                 schemaException.Message, isWarning ? XmlSeverityType.Warning : XmlSeverityType.Error);
500
501                         if (ValidationEventHandler != null)
502                                 ValidationEventHandler (this, e);
503
504                         else if (e.Severity == XmlSeverityType.Error)
505                                 throw e.Exception;
506                 }
507
508                 private XsElement FindElement (string name, string ns)
509                 {
510                         return (XsElement) schemas.GlobalElements [new XmlQualifiedName (name, ns)];
511                 }
512
513                 private XmlSchemaType FindType (XmlQualifiedName qname)
514                 {
515                         return (XmlSchemaType) schemas.GlobalTypes [qname];
516                 }
517
518                 private void ValidateStartElementParticle ()
519                 {
520                         if (Context.State == null)
521                                 return;
522                         Context.XsiType = null;
523                         state.CurrentElement = null;
524                         Context.EvaluateStartElement (reader.LocalName,
525                                 reader.NamespaceURI);
526                         if (Context.IsInvalid)
527                                 HandleError ("Invalid start element: " + reader.NamespaceURI + ":" + reader.LocalName);
528
529                         Context.SetElement (state.CurrentElement);
530                 }
531
532                 private void ValidateEndElementParticle ()
533                 {
534                         if (Context.State != null) {
535                                 if (!Context.EvaluateEndElement ()) {
536                                         HandleError ("Invalid end element: " + reader.Name);
537                                 }
538                         }
539                         state.PopContext ();
540                 }
541
542                 // Utility for missing validation completion related to child items.
543                 private void ValidateCharacters ()
544                 {
545                         if (xsiNilDepth >= 0 && xsiNilDepth < reader.Depth)
546                                 HandleError ("Element item appeared, while current element context is nil.");
547
548                         if (shouldValidateCharacters)
549                                 storedCharacters.Append (reader.Value);
550                 }
551
552                 // Utility for missing validation completion related to child items.
553                 private void ValidateEndCharacters ()
554                 {
555                         if (shouldValidateCharacters)
556                                 ValidateEndCharactersCore ();
557                         shouldValidateCharacters = false;
558                         storedCharacters.Length = 0;
559                 }
560
561                 private void ValidateEndCharactersCore ()
562                 {
563                         if (Context.ActualType == null)
564                                 return;
565
566                         string value = storedCharacters.ToString ();
567
568                         if (value.Length == 0) {
569                                 // 3.3.4 Element Locally Valid (Element) 5.1.2
570                                 if (Context.Element != null) {
571                                         if (Context.Element.ValidatedDefaultValue != null)
572                                                 value = Context.Element.ValidatedDefaultValue;
573                                 }                                       
574                         }
575
576                         XsDatatype dt = Context.ActualType as XsDatatype;
577                         SimpleType st = Context.ActualType as SimpleType;
578                         if (dt == null) {
579                                 if (st != null) {
580                                         dt = st.Datatype;
581                                 } else {
582                                         ComplexType ct = Context.ActualType as ComplexType;
583                                         dt = ct.Datatype;
584                                         switch (ct.ContentType) {
585                                         case XmlSchemaContentType.ElementOnly:
586                                         case XmlSchemaContentType.Empty:
587                                                 if (value.Length > 0)
588                                                         HandleError ("Character content not allowed.");
589                                                 break;
590                                         }
591                                 }
592                         }
593                         if (dt != null) {
594                                 // 3.3.4 Element Locally Valid (Element) :: 5.2.2.2. Fixed value constraints
595                                 if (Context.Element != null && Context.Element.ValidatedFixedValue != null)
596                                         if (value != Context.Element.ValidatedFixedValue)
597                                                 HandleError ("Fixed value constraint was not satisfied.");
598                                 AssessStringValid (st, dt, value);
599                         }
600
601 #region Key Constraints
602                         if (checkKeyConstraints)
603                                 ValidateSimpleContentIdentity (dt, value);
604 #endregion
605
606                         shouldValidateCharacters = false;
607                 }
608
609 #region Key Constraints
610                 private void ValidateSimpleContentIdentity (
611                         XmlSchemaDatatype dt, string value)
612                 {
613                         // Identity field value
614                         if (currentKeyFieldConsumers != null) {
615                                 while (this.currentKeyFieldConsumers.Count > 0) {
616                                         XsdKeyEntryField field = this.currentKeyFieldConsumers [0] as XsdKeyEntryField;
617                                         if (field.Identity != null)
618                                                 HandleError ("Two or more identical field was found. Former value is '" + field.Identity + "' .");
619                                         object identity = null; // This means empty value
620                                         if (dt != null) {
621                                                 try {
622                                                         identity = dt.ParseValue (value, NameTable, ParserContext.NamespaceManager);
623                                                 } catch (Exception ex) { // FIXME: (wishlist) This is bad manner ;-(
624                                                         HandleError ("Identity value is invalid against its data type " + dt.TokenizedType, ex);
625                                                 }
626                                         }
627                                         if (identity == null)
628                                                 identity = value;
629
630                                         if (!field.SetIdentityField (identity, reader.Depth == xsiNilDepth, dt as XsdAnySimpleType, this.Depth, (IXmlLineInfo) this))
631                                                 HandleError ("Two or more identical key value was found: '" + value + "' .");
632                                         this.currentKeyFieldConsumers.RemoveAt (0);
633                                 }
634                         }
635                 }
636 #endregion
637
638                 // 3.14.4 String Valid 
639                 private void AssessStringValid (SimpleType st,
640                         XsDatatype dt, string value)
641                 {
642                         XsDatatype validatedDatatype = dt;
643                         if (st != null) {
644                                 string normalized = validatedDatatype.Normalize (value);
645                                 string [] values;
646                                 XsDatatype itemDatatype;
647                                 SimpleType itemSimpleType;
648                                 switch (st.DerivedBy) {
649                                 case XmlSchemaDerivationMethod.List:
650                                         SimpleTypeList listContent = st.Content as SimpleTypeList;
651                                         values = normalized.Split (XmlChar.WhitespaceChars);
652                                         itemDatatype = listContent.ValidatedListItemType as XsDatatype;
653                                         itemSimpleType = listContent.ValidatedListItemType as SimpleType;
654                                         for (int vi = 0; vi < values.Length; vi++) {
655                                                 string each = values [vi];
656                                                 if (each == String.Empty)
657                                                         continue;
658                                                 // validate against ValidatedItemType
659                                                 if (itemDatatype != null) {
660                                                         try {
661                                                                 itemDatatype.ParseValue (each, NameTable, ParserContext.NamespaceManager);
662                                                         } catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-(
663                                                                 HandleError ("List type value contains one or more invalid values.", ex);
664                                                                 break;
665                                                         }
666                                                 }
667                                                 else
668                                                         AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
669                                         }
670                                         break;
671                                 case XmlSchemaDerivationMethod.Union:
672                                         SimpleTypeUnion union = st.Content as SimpleTypeUnion;
673                                         {
674                                                 string each = normalized;
675                                                 // validate against ValidatedItemType
676                                                 bool passed = false;
677                                                 foreach (object eachType in union.ValidatedTypes) {
678                                                         itemDatatype = eachType as XsDatatype;
679                                                         itemSimpleType = eachType as SimpleType;
680                                                         if (itemDatatype != null) {
681                                                                 try {
682                                                                         itemDatatype.ParseValue (each, NameTable, ParserContext.NamespaceManager);
683                                                                 } catch (Exception) { // FIXME: (wishlist) better exception handling ;-(
684                                                                         continue;
685                                                                 }
686                                                         }
687                                                         else {
688                                                                 try {
689                                                                         AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
690                                                                 } catch (XmlSchemaException) {
691                                                                         continue;
692                                                                 }
693                                                         }
694                                                         passed = true;
695                                                         break;
696                                                 }
697                                                 if (!passed) {
698                                                         HandleError ("Union type value contains one or more invalid values.");
699                                                         break;
700                                                 }
701                                         }
702                                         break;
703                                 case XmlSchemaDerivationMethod.Restriction:
704                                         SimpleTypeRest str = st.Content as SimpleTypeRest;
705                                         // facet validation
706                                         if (str != null) {
707                                                 /* Don't forget to validate against inherited type's facets 
708                                                  * Could we simplify this by assuming that the basetype will also
709                                                  * be restriction?
710                                                  * */
711                                                  // mmm, will check later.
712                                                 SimpleType baseType = st.BaseXmlSchemaType as SimpleType;
713                                                 if (baseType != null) {
714                                                          AssessStringValid(baseType, dt, normalized);
715                                                 }
716                                                 if (!str.ValidateValueWithFacets (normalized, NameTable)) {
717                                                         HandleError ("Specified value was invalid against the facets.");
718                                                         break;
719                                                 }
720                                         }
721                                         validatedDatatype = st.Datatype;
722                                         break;
723                                 }
724                         }
725                         if (validatedDatatype != null) {
726                                 try {
727                                         validatedDatatype.ParseValue (value, NameTable, ParserContext.NamespaceManager);
728                                 } catch (Exception ex) {        // FIXME: (wishlist) It is bad manner ;-(
729                                         HandleError ("Invalidly typed data was specified.", ex);
730                                 }
731                         }
732                 }
733
734                 private object GetXsiType (string name)
735                 {
736                         object xsiType = null;
737                         XmlQualifiedName typeQName =
738                                 XmlQualifiedName.Parse (name, this);
739                         if (typeQName == ComplexType.AnyTypeName)
740                                 xsiType = ComplexType.AnyType;
741                         else if (XmlSchemaUtil.IsBuiltInDatatypeName (typeQName))
742                                 xsiType = XsDatatype.FromName (typeQName);
743                         else
744                                 xsiType = FindType (typeQName);
745                         return xsiType;
746                 }
747
748                 // It is common to ElementLocallyValid::4 and SchemaValidityAssessment::1.2.1.2.4
749                 private void AssessLocalTypeDerivationOK (object xsiType, object baseType, XmlSchemaDerivationMethod flag)
750                 {
751                         XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType;
752                         ComplexType baseComplexType = baseType as ComplexType;
753                         ComplexType xsiComplexType = xsiSchemaType as ComplexType;
754                         if (xsiType != baseType) {
755                                 // Extracted (not extraneous) check for 3.4.6 TypeDerivationOK.
756                                 if (baseComplexType != null)
757                                         flag |= baseComplexType.BlockResolved;
758                                 if (flag == XmlSchemaDerivationMethod.All) {
759                                         HandleError ("Prohibited element type substitution.");
760                                         return;
761                                 } else if (xsiSchemaType != null && (flag & xsiSchemaType.DerivedBy) != 0) {
762                                         HandleError ("Prohibited element type substitution.");
763                                         return;
764                                 }
765                         }
766
767                         if (xsiComplexType != null)
768                                 try {
769                                         xsiComplexType.ValidateTypeDerivationOK (baseType, null, null);
770                                 } catch (XmlSchemaException ex) {
771 //                                      HandleError ("Locally specified schema complex type derivation failed. " + ex.Message, ex);
772                                         HandleError (ex);
773                                 }
774                         else {
775                                 SimpleType xsiSimpleType = xsiType as SimpleType;
776                                 if (xsiSimpleType != null) {
777                                         try {
778                                                 xsiSimpleType.ValidateTypeDerivationOK (baseType, null, null, true);
779                                         } catch (XmlSchemaException ex) {
780 //                                              HandleError ("Locally specified schema simple type derivation failed. " + ex.Message, ex);
781                                                 HandleError (ex);
782                                         }
783                                 }
784                                 else if (xsiType is XsDatatype) {
785                                         // do nothing
786                                 }
787                                 else
788                                         HandleError ("Primitive data type cannot be derived type using xsi:type specification.");
789                         }
790                 }
791
792                 // Section 3.3.4 of the spec.
793                 private void AssessStartElementSchemaValidity ()
794                 {
795                         // If the reader is inside xsi:nil (and failed
796                         // on validation), then simply skip its content.
797                         if (xsiNilDepth >= 0 && xsiNilDepth < reader.Depth)
798                                 HandleError ("Element item appeared, while current element context is nil.");
799
800                         ValidateStartElementParticle ();
801
802                         string xsiNilValue = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace);
803                         if (xsiNilValue != null)
804                                 xsiNilValue = xsiNilValue.Trim (XmlChar.WhitespaceChars);
805                         bool isXsiNil = xsiNilValue == "true";
806                         if (isXsiNil && this.xsiNilDepth < 0)
807                                 xsiNilDepth = reader.Depth;
808
809                         // [Schema Validity Assessment (Element) 1.2]
810                         // Evaluate "local type definition" from xsi:type.
811                         // (See spec 3.3.4 Schema Validity Assessment (Element) 1.2.1.2.3.
812                         // Note that Schema Validity Assessment(Element) 1.2 takes
813                         // precedence than 1.1 of that.
814
815                         string xsiTypeName = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
816                         if (xsiTypeName != null) {
817                                 xsiTypeName = xsiTypeName.Trim (XmlChar.WhitespaceChars);
818                                 object xsiType = GetXsiType (xsiTypeName);
819                                 if (xsiType == null)
820                                         HandleError ("The instance type was not found: " + xsiTypeName + " .");
821                                 else {
822                                         XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType;
823                                         if (xsiSchemaType != null && this.Context.Element != null) {
824                                                 XmlSchemaType elemBaseType = Context.Element.ElementType as XmlSchemaType;
825                                                 if (elemBaseType != null && (xsiSchemaType.DerivedBy & elemBaseType.FinalResolved) != 0)
826                                                         HandleError ("The instance type is prohibited by the type of the context element.");
827                                                 if (elemBaseType != xsiType && (xsiSchemaType.DerivedBy & this.Context.Element.BlockResolved) != 0)
828                                                         HandleError ("The instance type is prohibited by the context element.");
829                                         }
830                                         ComplexType xsiComplexType = xsiType as ComplexType;
831                                         if (xsiComplexType != null && xsiComplexType.IsAbstract)
832                                                 HandleError ("The instance type is abstract: " + xsiTypeName + " .");
833                                         else {
834                                                 // If current schema type exists, then this xsi:type must be
835                                                 // valid extension of that type. See 1.2.1.2.4.
836                                                 if (Context.Element != null) {
837                                                         AssessLocalTypeDerivationOK (xsiType, Context.Element.ElementType, Context.Element.BlockResolved);
838                                                 }
839                                                 AssessStartElementLocallyValidType (xsiType);   // 1.2.2:
840                                                 Context.XsiType = xsiType;
841                                         }
842                                 }
843                         }
844
845                         // Create Validation Root, if not exist.
846                         // [Schema Validity Assessment (Element) 1.1]
847                         if (Context.Element == null) {
848                                 state.CurrentElement = FindElement (reader.LocalName, reader.NamespaceURI);
849                                 Context.SetElement (state.CurrentElement);
850                         }
851                         if (Context.Element != null) {
852                                 if (xsiTypeName == null) {
853                                         AssessElementLocallyValidElement (Context.Element, xsiNilValue);        // 1.1.2
854                                 }
855                         } else {
856                                 switch (state.ProcessContents) {
857                                 case ContentProc.Skip:
858                                         break;
859                                 case ContentProc.Lax:
860                                         break;
861                                 default:
862                                         if (xsiTypeName == null &&
863                                                 (schemas.Contains (reader.NamespaceURI) ||
864                                                 !schemas.MissedSubComponents (reader.NamespaceURI)))
865                                                 HandleError ("Element declaration for " + reader.LocalName + " is missing.");
866                                         break;
867                                 }
868                         }
869
870                         state.PushContext ();
871
872                         XsdValidationState next = null;
873                         if (state.ProcessContents == ContentProc.Skip)
874                                 skipValidationDepth = reader.Depth;
875                         else {
876                                 // create child particle state.
877                                 ComplexType xsComplexType = SchemaType as ComplexType;
878                                 if (xsComplexType != null)
879                                         next = state.Create (xsComplexType.ValidatableParticle);
880                                 else if (state.ProcessContents == ContentProc.Lax)
881                                         next = state.Create (XmlSchemaAny.AnyTypeContent);
882                                 else
883                                         next = state.Create (XmlSchemaParticle.Empty);
884                         }
885                         Context.State = next;
886
887 #region Key Constraints
888                         if (checkKeyConstraints)
889                                 AssessStartIdentityConstraints ();
890 #endregion
891
892                 }
893
894                 // 3.3.4 Element Locally Valid (Element)
895                 private void AssessElementLocallyValidElement (XsElement element, string xsiNilValue)
896                 {
897                         XmlQualifiedName qname = new XmlQualifiedName (reader.LocalName, reader.NamespaceURI);
898                         // 1.
899                         if (element == null)
900                                 HandleError ("Element declaration is required for " + qname);
901                         // 2.
902                         if (element.ActualIsAbstract)
903                                 HandleError ("Abstract element declaration was specified for " + qname);
904                         // 3.1.
905                         if (!element.ActualIsNillable && xsiNilValue != null)
906                                 HandleError ("This element declaration is not nillable: " + qname);
907                         // 3.2.
908                         // Note that 3.2.1 xsi:nil constraints are to be validated in
909                         else if (xsiNilValue == "true") {
910                                 // AssessElementSchemaValidity() and ValidateCharacters()
911
912                                 if (element.ValidatedFixedValue != null)
913                                         HandleError ("Schema instance nil was specified, where the element declaration for " + qname + "has fixed value constraints.");
914                         }
915                         // 4.
916                         string xsiType = reader.GetAttribute ("type", XmlSchema.InstanceNamespace);
917                         if (xsiType != null) {
918                                 Context.XsiType = GetXsiType (xsiType);
919                                 AssessLocalTypeDerivationOK (Context.XsiType, element.ElementType, element.BlockResolved);
920                         }
921                         else
922                                 Context.XsiType = null;
923
924                         // 5 Not all things cannot be assessed here.
925                         // It is common to 5.1 and 5.2
926                         if (element.ElementType != null)
927                                 AssessStartElementLocallyValidType (SchemaType);
928
929                         // 6. should be out from here.
930                         // See invokation of AssessStartIdentityConstraints().
931
932                         // 7 is going to be validated in Read() (in case of xmlreader's EOF).
933                 }
934
935                 // 3.3.4 Element Locally Valid (Type)
936                 private void AssessStartElementLocallyValidType (object schemaType)
937                 {
938                         if (schemaType == null) {       // 1.
939                                 HandleError ("Schema type does not exist.");
940                                 return;
941                         }
942                         ComplexType cType = schemaType as ComplexType;
943                         SimpleType sType = schemaType as SimpleType;
944                         if (sType != null) {
945                                 // 3.1.1.
946                                 while (reader.MoveToNextAttribute ()) {
947                                         if (reader.NamespaceURI == XmlNamespaceManager.XmlnsXmlns)
948                                                 continue;
949                                         if (reader.NamespaceURI != XmlSchema.InstanceNamespace)
950                                                 HandleError ("Current simple type cannot accept attributes other than schema instance namespace.");
951                                         switch (reader.LocalName) {
952                                         case "type":
953                                         case "nil":
954                                         case "schemaLocation":
955                                         case "noNamespaceSchemaLocation":
956                                                 break;
957                                         default:
958                                                 HandleError ("Unknown schema instance namespace attribute: " + reader.LocalName);
959                                                 break;
960                                         }
961                                 }
962                                 reader.MoveToElement ();
963                                 // 3.1.2 and 3.1.3 cannot be assessed here.
964                         } else if (cType != null) {
965                                 if (cType.IsAbstract) { // 2.
966                                         HandleError ("Target complex type is abstract.");
967                                         return;
968                                 }
969                                 // 3.2
970                                 AssessElementLocallyValidComplexType (cType);
971                         }
972                 }
973
974                 // 3.4.4 Element Locally Valid (Complex Type)
975                 private void AssessElementLocallyValidComplexType (ComplexType cType)
976                 {
977                         // 1.
978                         if (cType.IsAbstract)
979                                 HandleError ("Target complex type is abstract.");
980
981                         // 2 (xsi:nil and content prohibition)
982                         // See AssessStartElementSchemaValidity() and ValidateCharacters()
983
984                         // 3. attribute uses and 
985                         // 5. wild IDs
986                         if (reader.MoveToFirstAttribute ()) {
987                                 do {
988                                         switch (reader.NamespaceURI) {
989                                         case"http://www.w3.org/2000/xmlns/":
990                                         case XmlSchema.InstanceNamespace:
991                                                 continue;
992                                         }
993                                         XmlQualifiedName qname = new XmlQualifiedName (reader.LocalName, reader.NamespaceURI);
994                                         XmlSchemaObject attMatch = FindAttributeDeclaration (cType, qname);
995                                         if (attMatch == null)
996                                                 HandleError ("Attribute declaration was not found for " + qname);
997                                         XsAttribute attdecl = attMatch as XsAttribute;
998                                         if (attdecl != null) {
999                                                 AssessAttributeLocallyValidUse (attdecl);
1000                                                 AssessAttributeLocallyValid (attdecl);
1001                                         } // otherwise anyAttribute or null.
1002                                 } while (reader.MoveToNextAttribute ());
1003                                 reader.MoveToElement ();
1004                         }
1005
1006                         // Collect default attributes.
1007                         // 4.
1008                         foreach (DictionaryEntry entry in cType.AttributeUses) {
1009                                 XsAttribute attr = (XsAttribute) entry.Value;
1010                                 if (reader [attr.QualifiedName.Name, attr.QualifiedName.Namespace] == null) {
1011                                         if (attr.ValidatedUse == XmlSchemaUse.Required && 
1012                                                 attr.ValidatedFixedValue == null)
1013                                                 HandleError ("Required attribute " + attr.QualifiedName + " was not found.");
1014                                         else if (attr.ValidatedDefaultValue != null || attr.ValidatedFixedValue != null)
1015                                                 defaultAttributesCache.Add (attr);
1016                                 }
1017                         }
1018                         if (defaultAttributesCache.Count == 0)
1019                                 defaultAttributes = emptyAttributeArray;
1020                         else
1021                                 defaultAttributes = (XsAttribute []) 
1022                                         defaultAttributesCache.ToArray (
1023                                                 typeof (XsAttribute));
1024                         defaultAttributesCache.Clear ();
1025                         // 5. wild IDs was already checked above.
1026                 }
1027
1028                 // Spec 3.10.4 Item Valid (Wildcard)
1029                 private static bool AttributeWildcardItemValid (XmlSchemaAnyAttribute anyAttr, XmlQualifiedName qname, string ns)
1030                 {
1031                         if (anyAttr.HasValueAny)
1032                                 return true;
1033                         if (anyAttr.HasValueOther && (anyAttr.TargetNamespace == "" || ns != anyAttr.TargetNamespace))
1034                                 return true;
1035                         if (anyAttr.HasValueTargetNamespace && ns == anyAttr.TargetNamespace)
1036                                 return true;
1037                         if (anyAttr.HasValueLocal && ns == "")
1038                                 return true;
1039                         for (int i = 0; i < anyAttr.ResolvedNamespaces.Count; i++)
1040                                 if (anyAttr.ResolvedNamespaces [i] == ns)
1041                                         return true;
1042                         return false;
1043                 }
1044
1045                 private XmlSchemaObject FindAttributeDeclaration (
1046                         ComplexType cType,
1047                         XmlQualifiedName qname)
1048                 {
1049                         XmlSchemaObject result = cType.AttributeUses [qname];
1050                         if (result != null)
1051                                 return result;
1052                         if (cType.AttributeWildcard == null)
1053                                 return null;
1054
1055                         if (!AttributeWildcardItemValid (cType.AttributeWildcard, qname, reader.NamespaceURI))
1056                                 return null;
1057
1058                         if (cType.AttributeWildcard.ResolvedProcessContents == ContentProc.Skip)
1059                                 return cType.AttributeWildcard;
1060                         XsAttribute attr = schemas.GlobalAttributes [qname] as XsAttribute;
1061                         if (attr != null)
1062                                 return attr;
1063                         if (cType.AttributeWildcard.ResolvedProcessContents == ContentProc.Lax)
1064                                 return cType.AttributeWildcard;
1065                         else
1066                                 return null;
1067                 }
1068
1069                 // 3.2.4 Attribute Locally Valid and 3.4.4
1070                 private void AssessAttributeLocallyValid (XsAttribute attr)
1071                 {
1072                         // 2. - 4.
1073                         if (attr.AttributeType == null)
1074                                 HandleError ("Attribute type is missing for " + attr.QualifiedName);
1075                         XsDatatype dt = attr.AttributeType as XsDatatype;
1076                         if (dt == null)
1077                                 dt = ((SimpleType) attr.AttributeType).Datatype;
1078                         // It is a bit heavy process, so let's omit as long as possible ;-)
1079                         if (dt != SimpleType.AnySimpleType || attr.ValidatedFixedValue != null) {
1080                                 string normalized = dt.Normalize (reader.Value);
1081                                 object parsedValue = null;
1082                                 try {
1083                                         parsedValue = dt.ParseValue (normalized, reader.NameTable, this.ParserContext.NamespaceManager);
1084                                 } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
1085                                         HandleError ("Attribute value is invalid against its data type " + dt.TokenizedType, ex);
1086                                 }
1087                                 if (attr.ValidatedFixedValue != null && attr.ValidatedFixedValue != normalized) {
1088                                         HandleError ("The value of the attribute " + attr.QualifiedName + " does not match with its fixed value.");
1089                                         parsedValue = dt.ParseValue (attr.ValidatedFixedValue, reader.NameTable, this.ParserContext.NamespaceManager);
1090                                 }
1091 #region ID Constraints
1092                                 if (this.checkIdentity)
1093                                         AssessEachAttributeIdentityConstraint (dt, parsedValue);
1094 #endregion
1095                         }
1096                 }
1097
1098                 // 3.4.4-5 wild IDs
1099                 private void AssessEachAttributeIdentityConstraint (
1100                         XsDatatype dt, object parsedValue)
1101                 {
1102                         // Validate identity constraints.
1103                         string str = parsedValue as string;
1104                         switch (dt.TokenizedType) {
1105                         case XmlTokenizedType.ID:
1106                                 if (thisElementId != null)
1107                                         HandleError ("ID type attribute was already assigned in the containing element.");
1108                                 thisElementId = str;
1109                                 if (idList.Contains (str))
1110                                         HandleError ("Duplicate ID value was found.");
1111                                 else
1112                                         idList.Add (str, str);
1113                                 if (MissingIDReferences.Contains (str))
1114                                         MissingIDReferences.Remove (str);
1115                                 break;
1116                         case XmlTokenizedType.IDREF:
1117                                 if (!idList.Contains (str))
1118                                         MissingIDReferences.Add (str);
1119                                 break;
1120                         case XmlTokenizedType.IDREFS:
1121                                 string [] idrefs = (string []) parsedValue;
1122                                 for (int i = 0; i < idrefs.Length; i++) {
1123                                         string id = idrefs [i];
1124                                         if (!idList.Contains (id))
1125                                                 MissingIDReferences.Add (id);
1126                                 }
1127                                 break;
1128                         }
1129                 }
1130
1131                 private void AssessAttributeLocallyValidUse (XsAttribute attr)
1132                 {
1133                         // This is extra check than spec 3.5.4
1134                         if (attr.ValidatedUse == XmlSchemaUse.Prohibited)
1135                                 HandleError ("Attribute " + attr.QualifiedName + " is prohibited in this context.");
1136                 }
1137
1138                 private void AssessEndElementSchemaValidity ()
1139                 {
1140                         ValidateEndElementParticle ();  // validate against childrens' state.
1141
1142                         ValidateEndCharacters ();
1143
1144                         // 3.3.4 Assess ElementLocallyValidElement 5: value constraints.
1145                         // 3.3.4 Assess ElementLocallyValidType 3.1.3. = StringValid(3.14.4)
1146                         // => ValidateEndCharacters().
1147
1148 #region Key Constraints
1149                         if (checkKeyConstraints)
1150                                 ValidateEndElementKeyConstraints ();
1151 #endregion
1152
1153                         // Reset xsi:nil, if required.
1154                         if (xsiNilDepth == reader.Depth)
1155                                 xsiNilDepth = -1;
1156                 }
1157
1158 #region Key Constraints
1159                 private void ValidateEndElementKeyConstraints ()
1160                 {
1161                         // Reset Identity constraints.
1162                         for (int i = 0; i < keyTables.Count; i++) {
1163                                 XsdKeyTable seq = this.keyTables [i] as XsdKeyTable;
1164                                 if (seq.StartDepth == reader.Depth) {
1165                                         EndIdentityValidation (seq);
1166                                 } else {
1167                                         for (int k = 0; k < seq.Entries.Count; k++) {
1168                                                 XsdKeyEntry entry = seq.Entries [k] as XsdKeyEntry;
1169                                                 // Remove finished (maybe key not found) entries.
1170                                                 if (entry.StartDepth == reader.Depth) {
1171                                                         if (entry.KeyFound)
1172                                                                 seq.FinishedEntries.Add (entry);
1173                                                         else if (entry.KeySequence.SourceSchemaIdentity is XmlSchemaKey)
1174                                                                 HandleError ("Key sequence is missing.");
1175                                                         seq.Entries.RemoveAt (k);
1176                                                         k--;
1177                                                 }
1178                                                 // Pop validated key depth to find two or more fields.
1179                                                 else {
1180                                                         for (int j = 0; j < entry.KeyFields.Count; j++) {
1181                                                                 XsdKeyEntryField kf = entry.KeyFields [j];
1182                                                                 if (!kf.FieldFound && kf.FieldFoundDepth == reader.Depth) {
1183                                                                         kf.FieldFoundDepth = 0;
1184                                                                         kf.FieldFoundPath = null;
1185                                                                 }
1186                                                         }
1187                                                 }
1188                                         }
1189                                 }
1190                         }
1191                         for (int i = 0; i < keyTables.Count; i++) {
1192                                 XsdKeyTable seq = this.keyTables [i] as XsdKeyTable;
1193                                 if (seq.StartDepth == reader.Depth) {
1194                                         keyTables.RemoveAt (i);
1195                                         i--;
1196                                 }
1197                         }
1198                 }
1199
1200                 // 3.11.4 Identity Constraint Satisfied
1201                 private void AssessStartIdentityConstraints ()
1202                 {
1203                         if (tmpKeyrefPool != null)
1204                                 tmpKeyrefPool.Clear ();
1205                         if (Context.Element != null && Context.Element.Constraints.Count > 0) {
1206                                 // (a) Create new key sequences, if required.
1207                                 for (int i = 0; i < Context.Element.Constraints.Count; i++) {
1208                                         XmlSchemaIdentityConstraint ident = (XmlSchemaIdentityConstraint) Context.Element.Constraints [i];
1209                                         XsdKeyTable seq = CreateNewKeyTable (ident);
1210                                         if (ident is XmlSchemaKeyref) {
1211                                                 if (tmpKeyrefPool == null)
1212                                                         tmpKeyrefPool = new ArrayList ();
1213                                                 tmpKeyrefPool.Add (seq);
1214                                         }
1215                                 }
1216                         }
1217
1218                         // (b) Evaluate current key sequences.
1219                         for (int i = 0; i < keyTables.Count; i++) {
1220                                 XsdKeyTable seq  = (XsdKeyTable) keyTables [i];
1221                                 if (seq.SelectorMatches (this.elementQNameStack, reader) != null) {
1222                                         // creates and registers new entry.
1223                                         XsdKeyEntry entry = new XsdKeyEntry (seq, reader.Depth, reader as IXmlLineInfo);
1224                                         seq.Entries.Add (entry);
1225                                 }
1226                         }
1227
1228                         // (c) Evaluate field paths.
1229                         for (int i = 0; i < keyTables.Count; i++) {
1230                                 XsdKeyTable seq  = (XsdKeyTable) keyTables [i];
1231                                 // If possible, create new field entry candidates.
1232                                 for (int j = 0; j < seq.Entries.Count; j++) {
1233                                         XsdKeyEntry entry = seq.Entries [j];
1234                                         try {
1235                                                 entry.FieldMatches (this.elementQNameStack, this);
1236                                         } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
1237                                                 HandleError ("Identity field value is invalid against its data type.", ex);
1238                                         }
1239                                 }
1240                         }
1241                 }
1242
1243                 private XsdKeyTable CreateNewKeyTable (XmlSchemaIdentityConstraint ident)
1244                 {
1245                         XsdKeyTable seq = new XsdKeyTable (ident, this);
1246                         seq.StartDepth = reader.Depth;
1247                         this.keyTables.Add (seq);
1248                         return seq;
1249                 }
1250
1251                 private void EndIdentityValidation (XsdKeyTable seq)
1252                 {
1253                         ArrayList errors = new ArrayList ();
1254                         for (int i = 0; i < seq.Entries.Count; i++) {
1255                                 XsdKeyEntry entry = (XsdKeyEntry) seq.Entries [i];
1256                                 if (entry.KeyFound)
1257                                         continue;
1258                                 if (seq.SourceSchemaIdentity is XmlSchemaKey)
1259                                         errors.Add ("line " + entry.SelectorLineNumber + "position " + entry.SelectorLinePosition);
1260                         }
1261                         if (errors.Count > 0)
1262                                 HandleError ("Invalid identity constraints were found. Key was not found. "
1263                                         + String.Join (", ", errors.ToArray (typeof (string)) as string []));
1264
1265                         errors.Clear ();
1266                         // Find reference target
1267                         XmlSchemaKeyref xsdKeyref = seq.SourceSchemaIdentity as XmlSchemaKeyref;
1268                         if (xsdKeyref != null) {
1269                                 for (int i = this.keyTables.Count - 1; i >= 0; i--) {
1270                                         XsdKeyTable target = this.keyTables [i] as XsdKeyTable;
1271                                         if (target.SourceSchemaIdentity == xsdKeyref.Target) {
1272                                                 seq.ReferencedKey = target;
1273                                                 for (int j = 0; j < seq.FinishedEntries.Count; j++) {
1274                                                         XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [j];
1275                                                         for (int k = 0; k < target.FinishedEntries.Count; k++) {
1276                                                                 XsdKeyEntry targetEntry = (XsdKeyEntry) target.FinishedEntries [k];
1277                                                                 if (entry.CompareIdentity (targetEntry)) {
1278                                                                         entry.KeyRefFound = true;
1279                                                                         break;
1280                                                                 }
1281                                                         }
1282                                                 }
1283                                         }
1284                                 }
1285                                 if (seq.ReferencedKey == null)
1286                                         HandleError ("Target key was not found.");
1287                                 for (int i = 0; i < seq.FinishedEntries.Count; i++) {
1288                                         XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [i];
1289                                         if (!entry.KeyRefFound)
1290                                                 errors.Add (" line " + entry.SelectorLineNumber + ", position " + entry.SelectorLinePosition);
1291                                 }
1292                                 if (errors.Count > 0)
1293                                         HandleError ("Invalid identity constraints were found. Referenced key was not found: "
1294                                                 + String.Join (" / ", errors.ToArray (typeof (string)) as string []));
1295                         }
1296                 }
1297 #endregion
1298
1299                 // Overrided Methods
1300
1301                 public override void Close ()
1302                 {
1303                         reader.Close ();
1304                 }
1305
1306                 public override string GetAttribute (int i)
1307                 {
1308                         switch (reader.NodeType) {
1309                         case XmlNodeType.XmlDeclaration:
1310                         case XmlNodeType.DocumentType:
1311                                 return reader.GetAttribute (i);
1312                         }
1313
1314                         if (reader.AttributeCount > i)
1315                                 reader.GetAttribute (i);
1316                         int defIdx = i - reader.AttributeCount;
1317                         if (i < AttributeCount)
1318                                 return defaultAttributes [defIdx].DefaultValue;
1319
1320                         throw new ArgumentOutOfRangeException ("i", i, "Specified attribute index is out of range.");
1321                 }
1322
1323                 public override string GetAttribute (string name)
1324                 {
1325                         switch (reader.NodeType) {
1326                         case XmlNodeType.XmlDeclaration:
1327                         case XmlNodeType.DocumentType:
1328                                 return reader.GetAttribute (name);
1329                         }
1330
1331                         string value = reader.GetAttribute (name);
1332                         if (value != null)
1333                                 return value;
1334
1335                         XmlQualifiedName qname = SplitQName (name);
1336                         return GetDefaultAttribute (qname.Name, qname.Namespace);
1337                 }
1338
1339                 private XmlQualifiedName SplitQName (string name)
1340                 {
1341                         if (!XmlChar.IsName (name))
1342                                 throw new ArgumentException ("Invalid name was specified.", "name");
1343
1344                         Exception ex = null;
1345                         XmlQualifiedName qname = XmlSchemaUtil.ToQName (reader, name, out ex);
1346                         if (ex != null)
1347                                 return XmlQualifiedName.Empty;
1348                         else
1349                                 return qname;
1350                 }
1351
1352                 public override string GetAttribute (string localName, string ns)
1353                 {
1354                         switch (reader.NodeType) {
1355                         case XmlNodeType.XmlDeclaration:
1356                         case XmlNodeType.DocumentType:
1357                                 return reader.GetAttribute (localName, ns);
1358                         }
1359
1360                         string value = reader.GetAttribute (localName, ns);
1361                         if (value != null)
1362                                 return value;
1363
1364                         return GetDefaultAttribute (localName, ns);
1365                 }
1366
1367                 private string GetDefaultAttribute (string localName, string ns)
1368                 {
1369                         int idx = this.FindDefaultAttribute (localName, ns);
1370                         if (idx < 0)
1371                                 return null;
1372                         string value = defaultAttributes [idx].ValidatedDefaultValue;
1373                         if (value == null)
1374                                 value = defaultAttributes [idx].ValidatedFixedValue;
1375                         return value;
1376                 }
1377
1378                 private int FindDefaultAttribute (string localName, string ns)
1379                 {
1380                         for (int i = 0; i < this.defaultAttributes.Length; i++) {
1381                                 XsAttribute attr = defaultAttributes [i];
1382                                 if (attr.QualifiedName.Name == localName &&
1383                                         (ns == null || attr.QualifiedName.Namespace == ns))
1384                                         return i;
1385                         }
1386                         return -1;
1387                 }
1388
1389                 bool IXmlLineInfo.HasLineInfo ()
1390                 {
1391                         return readerLineInfo != null && readerLineInfo.HasLineInfo ();
1392                 }
1393
1394                 public override string LookupNamespace (string prefix)
1395                 {
1396                         return reader.LookupNamespace (prefix);
1397                 }
1398
1399                 string IXmlNamespaceResolver.LookupNamespace (string prefix, bool atomizedNames)
1400                 {
1401                         IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
1402                         if (res != null)
1403                                 return res.LookupNamespace (prefix, atomizedNames);
1404                         else
1405                                 return reader.LookupNamespace (prefix);
1406                 }
1407
1408                 public override void MoveToAttribute (int i)
1409                 {
1410                         switch (reader.NodeType) {
1411                         case XmlNodeType.XmlDeclaration:
1412                         case XmlNodeType.DocumentType:
1413                                 reader.MoveToAttribute (i);
1414                                 return;
1415                         }
1416
1417                         currentAttrType = null;
1418                         if (i < reader.AttributeCount) {
1419                                 reader.MoveToAttribute (i);
1420                                 this.currentDefaultAttribute = -1;
1421                                 this.defaultAttributeConsumed = false;
1422                         }
1423
1424                         if (i < AttributeCount) {
1425                                 this.currentDefaultAttribute = i - reader.AttributeCount;
1426                                 this.defaultAttributeConsumed = false;
1427                         }
1428                         else
1429                                 throw new ArgumentOutOfRangeException ("i", i, "Attribute index is out of range.");
1430                 }
1431
1432                 public override bool MoveToAttribute (string name)
1433                 {
1434                         switch (reader.NodeType) {
1435                         case XmlNodeType.XmlDeclaration:
1436                         case XmlNodeType.DocumentType:
1437                                 return reader.MoveToAttribute (name);
1438                         }
1439
1440                         currentAttrType = null;
1441                         bool b = reader.MoveToAttribute (name);
1442                         if (b) {
1443                                 this.currentDefaultAttribute = -1;
1444                                 this.defaultAttributeConsumed = false;
1445                                 return true;
1446                         }
1447
1448                         return MoveToDefaultAttribute (name, null);
1449                 }
1450
1451                 public override bool MoveToAttribute (string localName, string ns)
1452                 {
1453                         switch (reader.NodeType) {
1454                         case XmlNodeType.XmlDeclaration:
1455                         case XmlNodeType.DocumentType:
1456                                 return reader.MoveToAttribute (localName, ns);
1457                         }
1458
1459                         currentAttrType = null;
1460                         bool b = reader.MoveToAttribute (localName, ns);
1461                         if (b) {
1462                                 this.currentDefaultAttribute = -1;
1463                                 this.defaultAttributeConsumed = false;
1464                                 return true;
1465                         }
1466
1467                         return MoveToDefaultAttribute (localName, ns);
1468                 }
1469
1470                 private bool MoveToDefaultAttribute (string localName, string ns)
1471                 {
1472                         int idx = this.FindDefaultAttribute (localName, ns);
1473                         if (idx < 0)
1474                                 return false;
1475                         currentDefaultAttribute = idx;
1476                         defaultAttributeConsumed = false;
1477                         return true;
1478                 }
1479
1480                 public override bool MoveToElement ()
1481                 {
1482                         currentDefaultAttribute = -1;
1483                         defaultAttributeConsumed = false;
1484                         currentAttrType = null;
1485                         return reader.MoveToElement ();
1486                 }
1487
1488                 public override bool MoveToFirstAttribute ()
1489                 {
1490                         switch (reader.NodeType) {
1491                         case XmlNodeType.XmlDeclaration:
1492                         case XmlNodeType.DocumentType:
1493                                 return reader.MoveToFirstAttribute ();
1494                         }
1495
1496                         currentAttrType = null;
1497                         if (reader.AttributeCount > 0) {
1498                                 bool b = reader.MoveToFirstAttribute ();
1499                                 if (b) {
1500                                         currentDefaultAttribute = -1;
1501                                         defaultAttributeConsumed = false;
1502                                 }
1503                                 return b;
1504                         }
1505
1506                         if (this.defaultAttributes.Length > 0) {
1507                                 currentDefaultAttribute = 0;
1508                                 defaultAttributeConsumed = false;
1509                                 return true;
1510                         }
1511                         else
1512                                 return false;
1513                 }
1514
1515                 public override bool MoveToNextAttribute ()
1516                 {
1517                         switch (reader.NodeType) {
1518                         case XmlNodeType.XmlDeclaration:
1519                         case XmlNodeType.DocumentType:
1520                                 return reader.MoveToNextAttribute ();
1521                         }
1522
1523                         currentAttrType = null;
1524                         if (currentDefaultAttribute >= 0) {
1525                                 if (defaultAttributes.Length == currentDefaultAttribute + 1)
1526                                         return false;
1527                                 currentDefaultAttribute++;
1528                                 defaultAttributeConsumed = false;
1529                                 return true;
1530                         }
1531
1532                         bool b = reader.MoveToNextAttribute ();
1533                         if (b) {
1534                                 currentDefaultAttribute = -1;
1535                                 defaultAttributeConsumed = false;
1536                                 return true;
1537                         }
1538
1539                         if (defaultAttributes.Length > 0) {
1540                                 currentDefaultAttribute = 0;
1541                                 defaultAttributeConsumed = false;
1542                                 return true;
1543                         }
1544                         else
1545                                 return false;
1546                 }
1547
1548                 private void ExamineAdditionalSchema ()
1549                 {
1550                         if (resolver == null)
1551                                 return;
1552                         XmlSchema schema = null;
1553                         string schemaLocation = reader.GetAttribute ("schemaLocation", XmlSchema.InstanceNamespace);
1554                         bool schemaAdded = false;
1555                         if (schemaLocation != null) {
1556                                 string [] tmp = null;
1557                                 try {
1558                                         schemaLocation = XsDatatype.FromName ("token", XmlSchema.Namespace).Normalize (schemaLocation);
1559                                         tmp = schemaLocation.Split (XmlChar.WhitespaceChars);
1560                                 } catch (Exception ex) {
1561                                         HandleError ("Invalid schemaLocation attribute format.", ex, true);
1562                                         tmp = new string [0];
1563                                 }
1564                                 if (tmp.Length % 2 != 0)
1565                                         HandleError ("Invalid schemaLocation attribute format.");
1566                                 for (int i = 0; i < tmp.Length; i += 2) {
1567                                         Uri absUri = null;
1568                                         XmlTextReader xtr = null;
1569                                         try {
1570                                                 absUri = new Uri ((this.BaseURI != "" ? new Uri (BaseURI) : null), tmp [i + 1]);
1571                                                 xtr = new XmlTextReader (
1572                                                         absUri.ToString (),
1573                                                         (Stream) resolver.GetEntity (absUri, null, typeof (Stream)),
1574                                                         NameTable);
1575                                                 schema = XmlSchema.Read (xtr, ValidationEventHandler);
1576                                         } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
1577                                                 HandleError ("Could not resolve schema location URI: " + absUri, null, true);
1578                                                 continue;
1579                                         } finally {
1580                                                 if (xtr != null)
1581                                                         xtr.Close ();
1582                                         }
1583                                         if (schema.TargetNamespace == null)
1584                                                 schema.TargetNamespace = tmp [i];
1585                                         else if (schema.TargetNamespace != tmp [i])
1586                                                 HandleError ("Specified schema has different target namespace.");
1587                                 }
1588                         }
1589                         if (schema != null) {
1590                                 if (!schemas.Contains (schema.TargetNamespace)) {
1591                                         schemaAdded = true;
1592                                         schemas.Add (schema);
1593                                 }
1594                         }
1595                         schema = null;
1596                         string noNsSchemaLocation = reader.GetAttribute ("noNamespaceSchemaLocation", XmlSchema.InstanceNamespace);
1597                         if (noNsSchemaLocation != null) {
1598                                 Uri absUri = null;
1599                                 XmlTextReader xtr = null;
1600                                 try {
1601                                         absUri = new Uri ((this.BaseURI != "" ? new Uri (BaseURI) : null), noNsSchemaLocation);
1602                                         xtr = new XmlTextReader (
1603                                                 absUri.ToString (),
1604                                                 (Stream) resolver.GetEntity (absUri, null, typeof (Stream)),
1605                                                 NameTable);
1606                                         schema = XmlSchema.Read (xtr, ValidationEventHandler);
1607                                 } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
1608                                         HandleError ("Could not resolve schema location URI: " + absUri, null, true);
1609                                 } finally {
1610                                         if (xtr != null)
1611                                                 xtr.Close ();
1612                                 }
1613                                 if (schema != null && schema.TargetNamespace != null)
1614                                         HandleError ("Specified schema has different target namespace.");
1615                         }
1616                         if (schema != null) {
1617                                 if (!schemas.Contains (schema.TargetNamespace)) {
1618                                         schemaAdded = true;
1619                                         schemas.Add (schema);
1620                                 }
1621                         }
1622                         // FIXME: should call Reprocess()?
1623                         if (schemaAdded)
1624                                 schemas.Compile ();
1625                 }
1626
1627                 private bool HasMissingIDReferences ()
1628                 {
1629                         return missingIDReferences != null
1630                                 && missingIDReferences.Count > 0;
1631                 }
1632
1633                 public override bool Read ()
1634                 {
1635                         currentDefaultAttribute = -1;
1636                         defaultAttributeConsumed = false;
1637                         currentAttrType = null;
1638 #region ID Constraints
1639                         if (this.checkIdentity)
1640                                 thisElementId = null;
1641 #endregion
1642                         defaultAttributes = emptyAttributeArray;
1643
1644                         bool result = reader.Read ();
1645 #region ID Constraints
1646                         // 3.3.4 ElementLocallyValidElement 7 = Root Valid.
1647                         if (!result && this.checkIdentity &&
1648                                 HasMissingIDReferences ())
1649                                 HandleError ("There are missing ID references: " +
1650                                         String.Join (" ",
1651                                         this.missingIDReferences.ToArray (typeof (string)) as string []));
1652 #endregion
1653
1654                         // FIXME: schemaLocation could be specified 
1655                         // at any Depth.
1656                         if (reader.Depth == 0 &&
1657                                 reader.NodeType == XmlNodeType.Element)
1658                                 ExamineAdditionalSchema ();
1659                         if (schemas.Count == 0)
1660                                 return result;
1661                         if (!schemas.IsCompiled)
1662                                 schemas.Compile ();
1663
1664                         switch (reader.NodeType) {
1665                         case XmlNodeType.Element:
1666 #region Key Constraints
1667                                 if (checkKeyConstraints)
1668                                         this.elementQNameStack.Add (new XmlQualifiedName (reader.LocalName, reader.NamespaceURI));
1669 #endregion
1670
1671                                 // If there is no schema information, then no validation is performed.
1672                                 if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth) {
1673                                         ValidateEndCharacters ();
1674                                         AssessStartElementSchemaValidity ();
1675                                 }
1676
1677                                 if (reader.IsEmptyElement)
1678                                         goto case XmlNodeType.EndElement;
1679                                 else
1680                                         shouldValidateCharacters = true;
1681                                 break;
1682                         case XmlNodeType.EndElement:
1683                                 if (reader.Depth == skipValidationDepth)
1684                                         skipValidationDepth = -1;
1685                                 else if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth)
1686                                         AssessEndElementSchemaValidity ();
1687
1688                                 if (checkKeyConstraints)
1689                                         elementQNameStack.RemoveAt (elementQNameStack.Count - 1);
1690                                 break;
1691
1692                         case XmlNodeType.CDATA:
1693                         case XmlNodeType.SignificantWhitespace:
1694                         case XmlNodeType.Text:
1695                                 // FIXME: does this check make sense?
1696                                 ComplexType ct = Context.ActualType as ComplexType;
1697                                 if (ct != null && storedCharacters.Length > 0) {
1698                                         switch (ct.ContentType) {
1699                                         case XmlSchemaContentType.ElementOnly:
1700                                         case XmlSchemaContentType.Empty:
1701                                                 HandleError ("Not allowed character content was found.");
1702                                                 break;
1703                                         }
1704                                 }
1705
1706                                 ValidateCharacters ();
1707                                 break;
1708                         }
1709
1710                         return result;
1711                 }
1712
1713                 public override bool ReadAttributeValue ()
1714                 {
1715                         if (currentDefaultAttribute < 0)
1716                                 return reader.ReadAttributeValue ();
1717
1718                         if (this.defaultAttributeConsumed)
1719                                 return false;
1720
1721                         defaultAttributeConsumed = true;
1722                         return true;
1723                 }
1724
1725 #if NET_1_0
1726                 public override string ReadInnerXml ()
1727                 {
1728                         // MS.NET 1.0 has a serious bug here. It skips validation.
1729                         return reader.ReadInnerXml ();
1730                 }
1731
1732                 public override string ReadOuterXml ()
1733                 {
1734                         // MS.NET 1.0 has a serious bug here. It skips validation.
1735                         return reader.ReadOuterXml ();
1736                 }
1737 #endif
1738
1739                 // XmlReader.ReadString() should call derived this.Read().
1740                 public override string ReadString ()
1741                 {
1742 #if NET_1_0
1743                         return reader.ReadString ();
1744 #else
1745                         return base.ReadString ();
1746 #endif
1747                 }
1748
1749                 // This class itself does not have this feature.
1750                 public override void ResolveEntity ()
1751                 {
1752                         reader.ResolveEntity ();
1753                 }
1754         }
1755
1756         internal class XsdValidationContext
1757         {
1758                 public XsdValidationContext ()
1759                 {
1760                 }
1761
1762                 // Some of them might be missing (See the spec section 5.3, and also 3.3.4).
1763                 public XsElement Element;
1764                 public object XsiType; // xsi:type
1765                 internal XsdValidationState State;
1766
1767                 // Note that it represents current element's type.
1768                 public object ActualType {
1769                         get {
1770                                 if (XsiType != null)
1771                                         return XsiType;
1772                                 else
1773                                         return Element != null ? Element.ElementType : null;
1774                         }
1775                 }
1776
1777                 public bool IsInvalid {
1778                         get { return State == XsdValidationState.Invalid; }
1779                 }
1780
1781                 public object Clone ()
1782                 {
1783                         return MemberwiseClone ();
1784                 }
1785
1786                 public void EvaluateStartElement (
1787                         string localName, string ns)
1788                 {
1789                         State = State.EvaluateStartElement (localName, ns);
1790                 }
1791
1792                 public bool EvaluateEndElement ()
1793                 {
1794                         return State.EvaluateEndElement ();
1795                 }
1796
1797                 public void SetElement (XsElement element)
1798                 {
1799                         Element = element;
1800                 }
1801         }
1802 }