Bläddra i källkod

2004-12-14 Atsushi Enomoto <[email protected]>

	* System.Xml.dll.sources : added XmlSchemaValidatingReader.

	* XmlSchemaValidatingReader.cs : new file; another XmlReader 
	  implementation that handles XSD validation. It is an reference
	  application for XmlSchemaValidator.
	* XsdValidatingReader.cs : Use readerLineInfo for IXmlLineInfo.

	* XmlReaderSettings.cs : XsdValidate and DtdValidate are now
	  ValidationType.
	* XmlReader.cs : In Create(), use XmlSchemaValidatingReader for xsd
	  validation.

	* XmlSchemaValidator.cs : implemented basic validation feature.



svn path=/trunk/mcs/; revision=37739
Atsushi Eno 21 år sedan
förälder
incheckning
2ff40096cb

+ 4 - 0
mcs/class/System.XML/ChangeLog

@@ -1,3 +1,7 @@
+2004-12-14  Atsushi Enomoto  <[email protected]>
+
+	* System.Xml.dll.sources : added XmlSchemaValidatingReader.
+
 2004-12-08  Atsushi Enomoto  <[email protected]>
 
 	* System.Xml.dll.sources : added XmlSchemaValidator.cs,

+ 7 - 0
mcs/class/System.XML/Mono.Xml.Schema/ChangeLog

@@ -1,3 +1,10 @@
+2004-12-14  Atsushi Enomoto <[email protected]>
+
+	* XmlSchemaValidatingReader.cs : new file; another XmlReader 
+	  implementation that handles XSD validation. It is an reference
+	  application for XmlSchemaValidator.
+	* XsdValidatingReader.cs : Use readerLineInfo for IXmlLineInfo.
+
 2004-12-14  Atsushi Enomoto <[email protected]>
 
 	* XsdIdentityState.cs : use validation exception for 2.0.

+ 858 - 0
mcs/class/System.XML/Mono.Xml.Schema/XmlSchemaValidatingReader.cs

@@ -0,0 +1,858 @@
+//
+// XmlSchemaValidatingReader.cs
+//
+// Author:
+//	Atsushi Enomoto <[email protected]>
+//
+// (C)2004 Novell Inc,
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections;
+using System.Text;
+using System.Xml;
+using System.Xml.Schema;
+using System.Xml.XPath;
+using Mono.Xml;
+
+#if NET_2_0
+
+using QName = System.Xml.XmlQualifiedName;
+using Form = System.Xml.Schema.XmlSchemaForm;
+using Use = System.Xml.Schema.XmlSchemaUse;
+using ContentType = System.Xml.Schema.XmlSchemaContentType;
+using Validity = System.Xml.Schema.XmlSchemaValidity;
+using ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags;
+using SOMList = System.Xml.Schema.XmlSchemaObjectCollection;
+using SOMObject = System.Xml.Schema.XmlSchemaObject;
+using XsElement = System.Xml.Schema.XmlSchemaElement;
+using XsAttr = System.Xml.Schema.XmlSchemaAttribute;
+using AttrGroup = System.Xml.Schema.XmlSchemaAttributeGroup;
+using AttrGroupRef = System.Xml.Schema.XmlSchemaAttributeGroupRef;
+using XsDatatype = System.Xml.Schema.XmlSchemaDatatype;
+using SchemaType = System.Xml.Schema.XmlSchemaType;
+using SimpleType = System.Xml.Schema.XmlSchemaSimpleType;
+using ComplexType = System.Xml.Schema.XmlSchemaComplexType;
+using SimpleModel = System.Xml.Schema.XmlSchemaSimpleContent;
+using SimpleExt = System.Xml.Schema.XmlSchemaSimpleContentExtension;
+using SimpleRst = System.Xml.Schema.XmlSchemaSimpleContentRestriction;
+using ComplexModel = System.Xml.Schema.XmlSchemaComplexContent;
+using ComplexExt = System.Xml.Schema.XmlSchemaComplexContentExtension;
+using ComplexRst = System.Xml.Schema.XmlSchemaComplexContentRestriction;
+using SimpleTypeRst = System.Xml.Schema.XmlSchemaSimpleTypeRestriction;
+using SimpleList = System.Xml.Schema.XmlSchemaSimpleTypeList;
+using SimpleUnion = System.Xml.Schema.XmlSchemaSimpleTypeUnion;
+using SchemaFacet = System.Xml.Schema.XmlSchemaFacet;
+using LengthFacet = System.Xml.Schema.XmlSchemaLengthFacet;
+using MinLengthFacet = System.Xml.Schema.XmlSchemaMinLengthFacet;
+using Particle = System.Xml.Schema.XmlSchemaParticle;
+using Sequence = System.Xml.Schema.XmlSchemaSequence;
+using Choice = System.Xml.Schema.XmlSchemaChoice;
+using ValException = System.Xml.Schema.XmlSchemaValidationException;
+
+namespace Mono.Xml.Schema
+{
+	internal class XmlSchemaValidatingReader : XmlReader, IXmlLineInfo
+	{
+		static readonly XsAttr [] emptyAttributeArray =
+			new XsAttr [0];
+
+		#region Instance Fields
+
+		XmlReader reader;
+		XmlSchemaSet schemas;
+		XmlSchemaValidator v;
+		XmlValueGetter getter;
+		XmlSchemaInfo xsinfo;
+		IXmlLineInfo readerLineInfo;
+		ValidationType validationType;
+//		IHasXmlSchemaInfo sourceReaderSchemaInfo;
+		int startDepth;
+
+		StringBuilder tmpBuilder = new StringBuilder ();
+
+		XsAttr [] defaultAttributes = emptyAttributeArray;
+		int currentDefaultAttribute = -1;
+		ArrayList defaultAttributesCache = new ArrayList ();
+		bool defaultAttributeConsumed;
+		object currentAttrType;
+
+		// Extra for XmlSchemaValidtingReader
+		// (not in XsdValidatingReader)
+		XsElement element; // ... xsinfo.Element?
+		object xsiType; // ... xsinfo.SchemaType?
+
+		#endregion
+
+		public XmlSchemaValidatingReader (XmlReader reader, XmlSchemaSet schemas)
+		{
+			IXmlNamespaceResolver nsResolver = reader as IXmlNamespaceResolver;
+			if (nsResolver == null)
+				throw new ArgumentException ("Argument XmlReader must implement IXmlNamespaceResolver.");
+
+			this.reader = reader;
+			if (schemas == null)
+				schemas = new XmlSchemaSet ();
+			this.schemas = schemas;
+			v = new XmlSchemaValidator (
+				reader.NameTable,
+				schemas,
+				nsResolver,
+				ValidationFlags.IgnoreValidationWarnings
+				| ValidationFlags.IgnoreSchemaLocation
+				| ValidationFlags.IgnoreInlineSchema);
+
+			readerLineInfo = reader as IXmlLineInfo;
+			startDepth = reader.Depth;
+			getter = delegate () { return Value; };
+			xsinfo = new XmlSchemaInfo (); // transition cache
+			v.LineInfoProvider = this;
+			v.ValidationEventSender = reader;
+#if !MS_NET
+			v.XmlResolver = schemas.XmlResolver;
+#endif
+			v.SourceUri = reader.BaseURI; // FIXME: it is in fact not in MS.NET.
+			v.Initialize ();
+		}
+
+		public XmlSchemaValidatingReader (XmlReader reader,
+			XmlReaderSettings settings)
+		{
+			IXmlNamespaceResolver nsResolver = reader as IXmlNamespaceResolver;
+			if (nsResolver == null)
+				throw new ArgumentException ("Argument XmlReader must implement IXmlNamespaceResolver.");
+
+			schemas = settings.Schemas;
+			if (schemas == null)
+				schemas = new XmlSchemaSet ();
+
+			this.reader = reader;
+			v = new XmlSchemaValidator (
+				reader.NameTable,
+				schemas,
+				nsResolver,
+				settings.ValidationFlags);
+
+			readerLineInfo = reader as IXmlLineInfo;
+			startDepth = reader.Depth;
+			getter = delegate () { return Value; };
+			v.LineInfoProvider = this;
+			v.ValidationEventSender = reader;
+#if !MS_NET
+			if (settings != null && settings.Schemas != null)
+				v.XmlResolver = settings.Schemas.XmlResolver;
+#endif
+			v.Initialize ();
+		}
+
+		public XmlSchemaValidatingReader (
+			XPathNavigator navigator,
+			XmlSchemaSet schemas,
+			ValidationEventHandler handler)
+		{
+			this.reader = navigator.ReadSubtree ();
+			startDepth = reader.Depth;
+			IXmlSchemaInfo info = navigator.SchemaInfo;
+			SchemaType schemaType = info != null ?
+				info.SchemaType : null;
+
+			if (schemas == null && schemaType == null)
+				throw new ArgumentException ("Neither of XmlSchemaSet is specified, nor XPathNavigator does not provide schema type information on current node.");
+
+			if (schemas == null)
+				schemas = new XmlSchemaSet (reader.NameTable);
+
+			v = new XmlSchemaValidator (
+				navigator.NameTable,
+				schemas,
+				navigator,
+				ValidationFlags.IgnoreSchemaLocation |
+					ValidationFlags.IgnoreInlineSchema);
+			this.schemas = schemas;
+
+			readerLineInfo = navigator as IXmlLineInfo;
+			getter = delegate () { return Value; };
+			v.LineInfoProvider = this;
+			v.ValidationEventSender = navigator;
+#if !MS_NET
+			v.XmlResolver = schemas.XmlResolver; // internal getter
+#endif
+			v.Initialize (schemaType);
+		}
+
+		public ValidationEventHandler ValidationEventHandler;
+
+		public object ActualType {
+			get {
+				return xsiType != null ?
+					xsiType :
+					element != null ? element.ElementType : null;
+			}
+		}
+
+		// clear default attributes, MoveTo*Attribute() transitent
+		// fields and so on.
+		private void ResetStateOnRead ()
+		{
+			currentDefaultAttribute = -1;
+			defaultAttributeConsumed = false;
+			currentAttrType = null;
+			defaultAttributes = emptyAttributeArray;
+		}
+
+		#region Properties
+
+		public int LineNumber {
+			get { return readerLineInfo != null ? readerLineInfo.LineNumber : 0; }
+		}
+
+		public int LinePosition {
+			get { return readerLineInfo != null ? readerLineInfo.LinePosition : 0; }
+		}
+
+		public XmlSchemaSet Schemas {
+			get { return schemas; }
+		}
+
+		public object SchemaType {
+			get {
+				if (ReadState != ReadState.Interactive)
+					return null;
+
+				switch (NodeType) {
+				case XmlNodeType.Element:
+					if (ActualType != null)
+						return ActualType;
+					else
+						return null;//SourceReaderSchemaType;
+				case XmlNodeType.Attribute:
+					if (currentAttrType == null) {
+						ComplexType ct = ActualType as ComplexType;
+						if (ct != null) {
+							XsAttr attdef = ct.AttributeUses [new XmlQualifiedName (LocalName, NamespaceURI)] as XsAttr;
+							if (attdef != null)
+								currentAttrType = attdef.AttributeType;
+							return currentAttrType;
+						}
+//						currentAttrType = SourceReaderSchemaType;
+					}
+					return currentAttrType;
+				default:
+					return null;//SourceReaderSchemaType;
+				}
+			}
+		}
+
+		public ValidationType ValidationType {
+			get { return validationType; }
+			set {
+				if (ReadState != ReadState.Initial)
+					throw new InvalidOperationException ("ValidationType must be set before reading.");
+				validationType = value;
+			}
+		}
+
+		public IDictionary GetNamespacesInScope (XmlNamespaceScope scope)
+		{
+			IXmlNamespaceResolver resolver = reader as IXmlNamespaceResolver;
+			if (resolver == null)
+				throw new NotSupportedException ("The input XmlReader does not implement IXmlNamespaceResolver and thus this validating reader cannot collect in-scope namespaces.");
+			return resolver.GetNamespacesInScope (scope);
+		}
+
+		public string LookupPrefix (string ns)
+		{
+			return ((IXmlNamespaceResolver) this).LookupPrefix (ns, false);
+		}
+
+		public string LookupPrefix (string ns, bool atomizedNames)
+		{
+			IXmlNamespaceResolver resolver = reader as IXmlNamespaceResolver;
+			if (resolver == null)
+				throw new NotSupportedException ("The input XmlReader does not implement IXmlNamespaceResolver and thus this validating reader cannot execute namespace prefix lookup.");
+			return resolver.LookupPrefix (ns, atomizedNames);
+		}
+
+		// It is used only for independent XmlReader use, not for XmlValidatingReader.
+		public override object ReadTypedValue ()
+		{
+			return XmlSchemaUtil.ReadTypedValue (this, SchemaType, this as IXmlNamespaceResolver, tmpBuilder);
+		}
+
+		// Public Overriden Properties
+
+		public override int AttributeCount {
+			get {
+				return reader.AttributeCount + defaultAttributes.Length;
+			}
+		}
+
+		public override string BaseURI {
+			get { return reader.BaseURI; }
+		}
+
+		// If this class is used to implement XmlValidatingReader,
+		// it should be left to DTDValidatingReader. In other cases,
+		// it depends on the reader's ability.
+		public override bool CanResolveEntity {
+			get { return reader.CanResolveEntity; }
+		}
+
+		public override int Depth {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.Depth;
+				if (this.defaultAttributeConsumed)
+					return reader.Depth + 2;
+				return reader.Depth + 1;
+			}
+		}
+
+		public override bool EOF {
+			get { return reader.EOF; }
+		}
+
+		public override bool HasValue {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.HasValue;
+				return true;
+			}
+		}
+
+		public override bool IsDefault {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.IsDefault;
+				return true;
+			}
+		}
+
+		public override bool IsEmptyElement {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.IsEmptyElement;
+				return false;
+			}
+		}
+
+		public override string this [int i] {
+			get { return GetAttribute (i); }
+		}
+
+		public override string this [string name] {
+			get { return GetAttribute (name); }
+		}
+
+		public override string this [string localName, string ns] {
+			get { return GetAttribute (localName, ns); }
+		}
+
+		int IXmlLineInfo.LineNumber {
+			get { return readerLineInfo != null ? readerLineInfo.LineNumber : 0; }
+		}
+
+		int IXmlLineInfo.LinePosition {
+			get { return readerLineInfo != null ? readerLineInfo.LinePosition : 0; }
+		}
+
+		public override string LocalName {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.LocalName;
+				if (defaultAttributeConsumed)
+					return String.Empty;
+				return defaultAttributes [currentDefaultAttribute].QualifiedName.Name;
+			}
+		}
+
+		public override string Name {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.Name;
+				if (defaultAttributeConsumed)
+					return String.Empty;
+
+				XmlQualifiedName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
+				string prefix = Prefix;
+				if (prefix == String.Empty)
+					return qname.Name;
+				else
+					return String.Concat (prefix, ":", qname.Name);
+			}
+		}
+
+		public override string NamespaceURI {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.NamespaceURI;
+				if (defaultAttributeConsumed)
+					return String.Empty;
+				return defaultAttributes [currentDefaultAttribute].QualifiedName.Namespace;
+			}
+		}
+
+		public override XmlNameTable NameTable {
+			get { return reader.NameTable; }
+		}
+
+		public override XmlNodeType NodeType {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.NodeType;
+				if (defaultAttributeConsumed)
+					return XmlNodeType.Text;
+				return XmlNodeType.Attribute;
+			}
+		}
+
+		public XmlParserContext ParserContext {
+			get { return XmlSchemaUtil.GetParserContext (reader); }
+		}
+
+		public override string Prefix {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.Prefix;
+				if (defaultAttributeConsumed)
+					return String.Empty;
+				XmlQualifiedName qname = defaultAttributes [currentDefaultAttribute].QualifiedName;
+				string prefix = this.ParserContext.NamespaceManager.LookupPrefix (qname.Namespace, false);
+				if (prefix == null)
+					return String.Empty;
+				else
+					return prefix;
+			}
+		}
+
+		public override char QuoteChar {
+			get { return reader.QuoteChar; }
+		}
+
+		public override ReadState ReadState {
+			get { return reader.ReadState; }
+		}
+
+		public override string Value {
+			get {
+				if (currentDefaultAttribute < 0)
+					return reader.Value;
+				string value = defaultAttributes [currentDefaultAttribute].ValidatedDefaultValue;
+				if (value == null)
+					value = defaultAttributes [currentDefaultAttribute].ValidatedFixedValue;
+				return value;
+			}
+		}
+
+		public override string XmlLang {
+			get {
+				string xmlLang = reader.XmlLang;
+				if (xmlLang != null)
+					return xmlLang;
+				int idx = this.FindDefaultAttribute ("lang", XmlNamespaceManager.XmlnsXml);
+				if (idx < 0)
+					return null;
+				xmlLang = defaultAttributes [idx].ValidatedDefaultValue;
+				if (xmlLang == null)
+					xmlLang = defaultAttributes [idx].ValidatedFixedValue;
+				return xmlLang;
+			}
+		}
+
+		public override XmlSpace XmlSpace {
+			get {
+				XmlSpace space = reader.XmlSpace;
+				if (space != XmlSpace.None)
+					return space;
+				int idx = this.FindDefaultAttribute ("space", XmlNamespaceManager.XmlnsXml);
+				if (idx < 0)
+					return XmlSpace.None;
+				string spaceSpec = defaultAttributes [idx].ValidatedDefaultValue;
+				if (spaceSpec == null)
+					spaceSpec = defaultAttributes [idx].ValidatedFixedValue;
+				return (XmlSpace) Enum.Parse (typeof (XmlSpace), spaceSpec, false);
+			}
+		}
+		#endregion
+
+		#region Public Methods
+
+		// Overrided Methods
+
+		public override void Close ()
+		{
+			reader.Close ();
+		}
+
+		public override string GetAttribute (int i)
+		{
+			switch (reader.NodeType) {
+			case XmlNodeType.XmlDeclaration:
+			case XmlNodeType.DocumentType:
+				return reader.GetAttribute (i);
+			}
+
+			if (reader.AttributeCount > i)
+				reader.GetAttribute (i);
+			int defIdx = i - reader.AttributeCount;
+			if (i < AttributeCount)
+				return defaultAttributes [defIdx].DefaultValue;
+
+			throw new ArgumentOutOfRangeException ("i", i, "Specified attribute index is out of range.");
+		}
+
+		public override string GetAttribute (string name)
+		{
+			switch (reader.NodeType) {
+			case XmlNodeType.XmlDeclaration:
+			case XmlNodeType.DocumentType:
+				return reader.GetAttribute (name);
+			}
+
+			string value = reader.GetAttribute (name);
+			if (value != null)
+				return value;
+
+			XmlQualifiedName qname = SplitQName (name);
+			return GetDefaultAttribute (qname.Name, qname.Namespace);
+		}
+
+		private XmlQualifiedName SplitQName (string name)
+		{
+			XmlConvert.VerifyName (name);
+
+			Exception ex = null;
+			XmlQualifiedName qname = XmlSchemaUtil.ToQName (reader, name, out ex);
+			if (ex != null)
+				return XmlQualifiedName.Empty;
+			else
+				return qname;
+		}
+
+		public override string GetAttribute (string localName, string ns)
+		{
+			switch (reader.NodeType) {
+			case XmlNodeType.XmlDeclaration:
+			case XmlNodeType.DocumentType:
+				return reader.GetAttribute (localName, ns);
+			}
+
+			string value = reader.GetAttribute (localName, ns);
+			if (value != null)
+				return value;
+
+			return GetDefaultAttribute (localName, ns);
+		}
+
+		private string GetDefaultAttribute (string localName, string ns)
+		{
+			int idx = this.FindDefaultAttribute (localName, ns);
+			if (idx < 0)
+				return null;
+			string value = defaultAttributes [idx].ValidatedDefaultValue;
+			if (value == null)
+				value = defaultAttributes [idx].ValidatedFixedValue;
+			return value;
+		}
+
+		private int FindDefaultAttribute (string localName, string ns)
+		{
+			for (int i = 0; i < this.defaultAttributes.Length; i++) {
+				XsAttr attr = defaultAttributes [i];
+				if (attr.QualifiedName.Name == localName &&
+					(ns == null || attr.QualifiedName.Namespace == ns))
+					return i;
+			}
+			return -1;
+		}
+
+		bool IXmlLineInfo.HasLineInfo ()
+		{
+			return readerLineInfo != null && readerLineInfo.HasLineInfo ();
+		}
+
+		public override string LookupNamespace (string prefix)
+		{
+			return reader.LookupNamespace (prefix);
+		}
+
+		public override string LookupNamespace (string prefix, bool atomizedNames)
+		{
+			IXmlNamespaceResolver res = reader as IXmlNamespaceResolver;
+			if (res != null)
+				return res.LookupNamespace (prefix, atomizedNames);
+			else
+				return reader.LookupNamespace (prefix);
+		}
+
+		public override void MoveToAttribute (int i)
+		{
+			switch (reader.NodeType) {
+			case XmlNodeType.XmlDeclaration:
+			case XmlNodeType.DocumentType:
+				reader.MoveToAttribute (i);
+				return;
+			}
+
+			currentAttrType = null;
+			if (i < reader.AttributeCount) {
+				reader.MoveToAttribute (i);
+				this.currentDefaultAttribute = -1;
+				this.defaultAttributeConsumed = false;
+			}
+
+			if (i < AttributeCount) {
+				this.currentDefaultAttribute = i - reader.AttributeCount;
+				this.defaultAttributeConsumed = false;
+			}
+			else
+				throw new ArgumentOutOfRangeException ("i", i, "Attribute index is out of range.");
+		}
+
+		public override bool MoveToAttribute (string name)
+		{
+			switch (reader.NodeType) {
+			case XmlNodeType.XmlDeclaration:
+			case XmlNodeType.DocumentType:
+				return reader.MoveToAttribute (name);
+			}
+
+			currentAttrType = null;
+			bool b = reader.MoveToAttribute (name);
+			if (b) {
+				this.currentDefaultAttribute = -1;
+				this.defaultAttributeConsumed = false;
+				return true;
+			}
+
+			return MoveToDefaultAttribute (name, null);
+		}
+
+		public override bool MoveToAttribute (string localName, string ns)
+		{
+			switch (reader.NodeType) {
+			case XmlNodeType.XmlDeclaration:
+			case XmlNodeType.DocumentType:
+				return reader.MoveToAttribute (localName, ns);
+			}
+
+			currentAttrType = null;
+			bool b = reader.MoveToAttribute (localName, ns);
+			if (b) {
+				this.currentDefaultAttribute = -1;
+				this.defaultAttributeConsumed = false;
+				return true;
+			}
+
+			return MoveToDefaultAttribute (localName, ns);
+		}
+
+		private bool MoveToDefaultAttribute (string localName, string ns)
+		{
+			int idx = this.FindDefaultAttribute (localName, ns);
+			if (idx < 0)
+				return false;
+			currentDefaultAttribute = idx;
+			defaultAttributeConsumed = false;
+			return true;
+		}
+
+		public override bool MoveToElement ()
+		{
+			currentDefaultAttribute = -1;
+			defaultAttributeConsumed = false;
+			currentAttrType = null;
+			return reader.MoveToElement ();
+		}
+
+		public override bool MoveToFirstAttribute ()
+		{
+			switch (reader.NodeType) {
+			case XmlNodeType.XmlDeclaration:
+			case XmlNodeType.DocumentType:
+				return reader.MoveToFirstAttribute ();
+			}
+
+			currentAttrType = null;
+			if (reader.AttributeCount > 0) {
+				bool b = reader.MoveToFirstAttribute ();
+				if (b) {
+					currentDefaultAttribute = -1;
+					defaultAttributeConsumed = false;
+				}
+				return b;
+			}
+
+			if (this.defaultAttributes.Length > 0) {
+				currentDefaultAttribute = 0;
+				defaultAttributeConsumed = false;
+				return true;
+			}
+			else
+				return false;
+		}
+
+		public override bool MoveToNextAttribute ()
+		{
+			switch (reader.NodeType) {
+			case XmlNodeType.XmlDeclaration:
+			case XmlNodeType.DocumentType:
+				return reader.MoveToNextAttribute ();
+			}
+
+			currentAttrType = null;
+			if (currentDefaultAttribute >= 0) {
+				if (defaultAttributes.Length == currentDefaultAttribute + 1)
+					return false;
+				currentDefaultAttribute++;
+				defaultAttributeConsumed = false;
+				return true;
+			}
+
+			bool b = reader.MoveToNextAttribute ();
+			if (b) {
+				currentDefaultAttribute = -1;
+				defaultAttributeConsumed = false;
+				return true;
+			}
+
+			if (defaultAttributes.Length > 0) {
+				currentDefaultAttribute = 0;
+				defaultAttributeConsumed = false;
+				return true;
+			}
+			else
+				return false;
+		}
+
+		public override bool Read ()
+		{
+			if (!reader.Read ()) {
+				v.EndValidation ();
+				return false;
+			}
+
+			ResetStateOnRead ();
+
+			switch (reader.NodeType) {
+			case XmlNodeType.Element:
+				// FIXME: find out what another overload means.
+				v.ValidateElement (reader.LocalName,
+					reader.NamespaceURI,
+					xsinfo);
+
+				if (reader.MoveToAttribute ("nil", XmlSchema.InstanceNamespace)) {
+					v.ValidateAttribute ("nil",
+						XmlSchema.InstanceNamespace,
+						getter, xsinfo);
+					reader.MoveToElement ();
+				}
+
+				if (reader.MoveToAttribute ("type", XmlSchema.InstanceNamespace)) {
+					v.ValidateAttribute ("type",
+						XmlSchema.InstanceNamespace,
+						getter, xsinfo);
+					reader.MoveToElement ();
+				}
+
+				if (reader.MoveToFirstAttribute ()) {
+					do {
+						if (reader.NamespaceURI == "http://www.w3.org/2000/xmlns/")
+							continue;
+						v.ValidateAttribute (
+							reader.LocalName,
+							reader.NamespaceURI,
+							getter,
+							xsinfo);
+					} while (reader.MoveToNextAttribute ());
+					reader.MoveToElement ();
+				}
+				v.ValidateEndOfAttributes ();
+				v.GetUnspecifiedDefaultAttributes (
+					defaultAttributesCache);
+				defaultAttributes = (XsAttr [])
+					defaultAttributesCache.ToArray (
+					typeof (XsAttr));
+
+				if (reader.IsEmptyElement)
+					goto case XmlNodeType.EndElement;
+				break;
+			case XmlNodeType.EndElement:
+				// FIXME: find out what another overload means.
+				v.ValidateEndElement (xsinfo);
+				break;
+			case XmlNodeType.Text:
+				v.ValidateText (getter);
+				break;
+			case XmlNodeType.SignificantWhitespace:
+			case XmlNodeType.Whitespace:
+				v.ValidateWhitespace (getter);
+				break;
+			}
+
+			return true;
+		}
+
+		public override bool ReadAttributeValue ()
+		{
+			if (currentDefaultAttribute < 0)
+				return reader.ReadAttributeValue ();
+
+			if (this.defaultAttributeConsumed)
+				return false;
+
+			defaultAttributeConsumed = true;
+			return true;
+		}
+
+#if NET_1_0
+		public override string ReadInnerXml ()
+		{
+			// MS.NET 1.0 has a serious bug here. It skips validation.
+			return ReadInnerXmlInternal ();
+		}
+
+		public override string ReadOuterXml ()
+		{
+			// MS.NET 1.0 has a serious bug here. It skips validation.
+			return ReadInnerXmlInternal ();
+		}
+
+		// XmlReader.ReadString() should call derived this.Read().
+		public override string ReadString ()
+		{
+			return ReadStringInternal ();
+		}
+#endif
+
+		// This class itself does not have this feature.
+		public override void ResolveEntity ()
+		{
+			reader.ResolveEntity ();
+		}
+
+		#endregion
+	}
+}
+
+#endif

+ 8 - 9
mcs/class/System.XML/Mono.Xml.Schema/XsdValidatingReader.cs

@@ -334,11 +334,11 @@ namespace Mono.Xml.Schema
 			get { return GetAttribute (localName, ns); }
 		}
 
-		int IXmlLineInfo.LineNumber {
+		public int LineNumber {
 			get { return readerLineInfo != null ? readerLineInfo.LineNumber : 0; }
 		}
 
-		int IXmlLineInfo.LinePosition {
+		public int LinePosition {
 			get { return readerLineInfo != null ? readerLineInfo.LinePosition : 0; }
 		}
 
@@ -826,11 +826,10 @@ namespace Mono.Xml.Schema
 				case ContentProc.Lax:
 					break;
 				default:
-					// FIXME: why is it not invalid if xsi:type exists?
 					if (xsiTypeName == null &&
 						(schemas.Contains (reader.NamespaceURI) ||
 						!schemas.MissedSubComponents (reader.NamespaceURI)))
-						HandleError ("Element declaration for " + reader.LocalName + " is missing.");
+						HandleError ("Element declaration for " + new QName (reader.LocalName, reader.NamespaceURI) + " is missing.");
 					break;
 				}
 			}
@@ -1121,7 +1120,7 @@ namespace Mono.Xml.Schema
 				XsdKeyTable seq  = (XsdKeyTable) keyTables [i];
 				if (seq.SelectorMatches (this.elementQNameStack, reader.Depth) != null) {
 					// creates and registers new entry.
-					XsdKeyEntry entry = new XsdKeyEntry (seq, reader.Depth, reader as IXmlLineInfo);
+					XsdKeyEntry entry = new XsdKeyEntry (seq, reader.Depth, readerLineInfo);
 					seq.Entries.Add (entry);
 				}
 			}
@@ -1146,7 +1145,7 @@ namespace Mono.Xml.Schema
 		private void ProcessKeyEntry (XsdKeyEntry entry)
 		{
 			bool isNil = XsiNilDepth == Depth;
-			entry.ProcessMatch (false, elementQNameStack, this, NameTable, BaseURI, SchemaType, ParserContext.NamespaceManager, this as IXmlLineInfo, Depth, null, null, null, isNil, CurrentKeyFieldConsumers);
+			entry.ProcessMatch (false, elementQNameStack, this, NameTable, BaseURI, SchemaType, ParserContext.NamespaceManager, readerLineInfo, Depth, null, null, null, isNil, CurrentKeyFieldConsumers);
 			if (MoveToFirstAttribute ()) {
 				try {
 					do {
@@ -1155,7 +1154,7 @@ namespace Mono.Xml.Schema
 						case XmlSchema.InstanceNamespace:
 							continue;
 						}
-						entry.ProcessMatch (true, elementQNameStack, this, NameTable, BaseURI, SchemaType, ParserContext.NamespaceManager, this as IXmlLineInfo, Depth, LocalName, NamespaceURI, Value, false, CurrentKeyFieldConsumers);
+						entry.ProcessMatch (true, elementQNameStack, this, NameTable, BaseURI, SchemaType, ParserContext.NamespaceManager, readerLineInfo, Depth, LocalName, NamespaceURI, Value, false, CurrentKeyFieldConsumers);
 					} while (MoveToNextAttribute ());
 				} finally {
 					MoveToElement ();
@@ -1191,7 +1190,7 @@ namespace Mono.Xml.Schema
 					if (identity == null)
 						identity = value;
 
-					if (!field.SetIdentityField (identity, reader.Depth == xsiNilDepth, dt as XsdAnySimpleType, this.Depth, (IXmlLineInfo) this))
+					if (!field.SetIdentityField (identity, reader.Depth == xsiNilDepth, dt as XsdAnySimpleType, this.Depth, readerLineInfo))
 						HandleError ("Two or more identical key value was found: '" + value + "' .");
 					this.currentKeyFieldConsumers.RemoveAt (0);
 				}
@@ -1346,7 +1345,7 @@ namespace Mono.Xml.Schema
 			return -1;
 		}
 
-		bool IXmlLineInfo.HasLineInfo ()
+		public bool HasLineInfo ()
 		{
 			return readerLineInfo != null && readerLineInfo.HasLineInfo ();
 		}

+ 4 - 0
mcs/class/System.XML/System.Xml.Schema/ChangeLog

@@ -1,3 +1,7 @@
+2004-12-14  Atsushi Enomoto <[email protected]>
+
+	* XmlSchemaValidator.cs : implemented basic validation feature.
+
 2004-12-14  Atsushi Enomoto <[email protected]>
 
 	* XmlSchemaUtil.cs : added FindAttributeDeclaration() and

+ 1153 - 37
mcs/class/System.XML/System.Xml.Schema/XmlSchemaValidator.cs

@@ -7,6 +7,34 @@
 // (C)2004 Novell Inc,
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+//
+// LAMESPEC:
+//	- There is no assurance that xsi:type precedes to any other attributes,
+//	  or xsi:type is not handled.
+//	- There is no SourceUri provision.
+//
+
 #if NET_2_0
 
 using System;
@@ -14,6 +42,7 @@ using System.Collections;
 using System.IO;
 using System.Text;
 using System.Xml;
+using Mono.Xml.Schema;
 
 using QName = System.Xml.XmlQualifiedName;
 using Form = System.Xml.Schema.XmlSchemaForm;
@@ -21,12 +50,14 @@ using Use = System.Xml.Schema.XmlSchemaUse;
 using ContentType = System.Xml.Schema.XmlSchemaContentType;
 using Validity = System.Xml.Schema.XmlSchemaValidity;
 using ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags;
+using ContentProc = System.Xml.Schema.XmlSchemaContentProcessing;
 using SOMList = System.Xml.Schema.XmlSchemaObjectCollection;
 using SOMObject = System.Xml.Schema.XmlSchemaObject;
-using Element = System.Xml.Schema.XmlSchemaElement;
-using Attr = System.Xml.Schema.XmlSchemaAttribute;
+using XsElement = System.Xml.Schema.XmlSchemaElement;
+using XsAttribute = System.Xml.Schema.XmlSchemaAttribute;
 using AttrGroup = System.Xml.Schema.XmlSchemaAttributeGroup;
 using AttrGroupRef = System.Xml.Schema.XmlSchemaAttributeGroupRef;
+using XsDatatype = System.Xml.Schema.XmlSchemaDatatype;
 using SimpleType = System.Xml.Schema.XmlSchemaSimpleType;
 using ComplexType = System.Xml.Schema.XmlSchemaComplexType;
 using SimpleModel = System.Xml.Schema.XmlSchemaSimpleContent;
@@ -35,21 +66,32 @@ using SimpleRst = System.Xml.Schema.XmlSchemaSimpleContentRestriction;
 using ComplexModel = System.Xml.Schema.XmlSchemaComplexContent;
 using ComplexExt = System.Xml.Schema.XmlSchemaComplexContentExtension;
 using ComplexRst = System.Xml.Schema.XmlSchemaComplexContentRestriction;
-using SimpleTypeRst = System.Xml.Schema.XmlSchemaSimpleTypeRestriction;
-using SimpleList = System.Xml.Schema.XmlSchemaSimpleTypeList;
-using SimpleUnion = System.Xml.Schema.XmlSchemaSimpleTypeUnion;
+using SimpleTypeRest = System.Xml.Schema.XmlSchemaSimpleTypeRestriction;
+using SimpleTypeList = System.Xml.Schema.XmlSchemaSimpleTypeList;
+using SimpleTypeUnion = System.Xml.Schema.XmlSchemaSimpleTypeUnion;
 using SchemaFacet = System.Xml.Schema.XmlSchemaFacet;
 using LengthFacet = System.Xml.Schema.XmlSchemaLengthFacet;
 using MinLengthFacet = System.Xml.Schema.XmlSchemaMinLengthFacet;
 using Particle = System.Xml.Schema.XmlSchemaParticle;
 using Sequence = System.Xml.Schema.XmlSchemaSequence;
 using Choice = System.Xml.Schema.XmlSchemaChoice;
+using ValException = System.Xml.Schema.XmlSchemaValidationException;
 
 
 namespace System.Xml.Schema
 {
 	public class XmlSchemaValidator
 	{
+		enum Transition {
+			None,
+			Content,
+			StartTag,
+			Finished
+		}
+
+		static readonly XsAttribute [] emptyAttributeArray =
+			new XsAttribute [0];
+
 		public XmlSchemaValidator (
 			XmlNameTable nameTable,
 			XmlSchemaSet schemas,
@@ -68,6 +110,7 @@ namespace System.Xml.Schema
 		object nominalEventSender;
 		IXmlLineInfo lineInfo;
 		IXmlNamespaceResolver nsResolver;
+		string sourceUri;
 
 		// These fields will be from XmlReaderSettings or 
 		// XPathNavigator.CheckValidity(). BTW, I think we could
@@ -75,20 +118,47 @@ namespace System.Xml.Schema
 		// XsdValidatingReader.
 		XmlNameTable nameTable;
 		XmlSchemaSet schemas;
-		XmlResolver xmlResolver;
+		XmlResolver xmlResolver = new XmlUrlResolver ();
 
 		// "partialValidationType". but not sure how it will be used.
 		SOMObject startType;
 
-		// Below are maybe from XmlReaderSettings, but XPathNavigator
+		// It is perhaps from XmlReaderSettings, but XPathNavigator
 		// does not have it.
 		ValidationFlags options;
 
+		// Validation state
+		Transition transition;
+		XsdParticleStateManager state;
+
+		ArrayList occuredAtts = new ArrayList ();
+		XsAttribute [] defaultAttributes = emptyAttributeArray;
+		ArrayList defaultAttributesCache = new ArrayList ();
+
+#region ID Constraints
+		XsdIDManager idManager = new XsdIDManager ();
+#endregion
+
+#region Key Constraints
+		ArrayList keyTables = new ArrayList ();
+		ArrayList currentKeyFieldConsumers = new ArrayList ();
+		ArrayList tmpKeyrefPool;
+#endregion
+		ArrayList elementQNameStack = new ArrayList ();
+
+		StringBuilder storedCharacters = new StringBuilder ();
+		bool shouldValidateCharacters;
+
+		int depth;
+		int xsiNilDepth = -1;
+		int skipValidationDepth = -1;
+
 		SOMObject currentType;
 
+
 		#endregion
 
-		#region Properties
+		#region Public properties
 
 		// Settable Properties
 
@@ -113,18 +183,46 @@ namespace System.Xml.Schema
 
 		[MonoTODO]
 		public string SourceUri {
-			get { throw new NotImplementedException (); }
+			get { return sourceUri; }
+			// FIXME: actually there seems no setter, but then
+			// it will never make sense.
+			set { sourceUri = value; }
+		}
+		#endregion
+
+		#region Private properties
+
+		private XsdValidationContext Context {
+			get { return state.Context; }
+		}
+
+		private bool IgnoreWarnings {
+			get { return (options & ValidationFlags
+				.IgnoreValidationWarnings) != 0; }
+		}
+
+		private bool IgnoreIdentity {
+			get { return (options & ValidationFlags
+				.IgnoreIdentityConstraints) != 0; }
 		}
+
 		#endregion
 
-		#region Methods
+		#region Public methods
 
 		// State Monitor
 
-		[MonoTODO]
 		public XmlSchemaAttribute [] GetExpectedAttributes ()
 		{
-			throw new NotImplementedException ();
+			ArrayList al = new ArrayList ();
+			ComplexType cType = Context.ActualType as ComplexType;
+			if (cType == null)
+				return emptyAttributeArray;
+			foreach (DictionaryEntry entry in cType.AttributeUses)
+				if (!occuredAtts.Contains ((QName) entry.Key))
+					al.Add (entry.Value);
+			return (XsAttribute [])
+				al.ToArray (typeof (XsAttribute));
 		}
 
 		[MonoTODO]
@@ -133,104 +231,1122 @@ namespace System.Xml.Schema
 			throw new NotImplementedException ();
 		}
 
-		[MonoTODO]
+		[MonoTODO ("check return type")]
 		public void GetUnspecifiedDefaultAttributes (ArrayList list)
 		{
-			throw new NotImplementedException ();
+			list.AddRange (defaultAttributes);
 		}
 
-		[MonoTODO]
 		public object FindID (string name)
 		{
-			throw new NotImplementedException ();
+			// It looks returning the element's local name (string)
+			return idManager.FindID (name);
 		}
 
 		// State Controller
 
-		public void Init ()
+		public void Initialize ()
 		{
-			Init (null);
+			Initialize (null);
 		}
 
-		public void Init (SOMObject startType)
+		[MonoTODO]
+		public void Initialize (SOMObject startType)
 		{
 			this.startType = startType;
+			transition = Transition.Content;
+			state = new XsdParticleStateManager ();
+			if (!schemas.IsCompiled)
+				schemas.Compile ();
 		}
 
 		// It must be called at the end of the validation (to check
 		// identity constraints etc.).
-		[MonoTODO]
 		public void EndValidation ()
 		{
-			throw new NotImplementedException ();
+			CheckState (Transition.Content);
+			transition = Transition.Finished;
+
+			if (schemas.Count == 0)
+				return;
+
+			if (depth > 0)
+				throw new InvalidOperationException (String.Format ("There are {0} open element(s). ValidateEndElement() must be called for each open element.", depth));
+
+			// 3.3.4 ElementLocallyValidElement 7 = Root Valid.
+			if (!IgnoreIdentity &&
+				idManager.HasMissingIDReferences ())
+				HandleError ("There are missing ID references: " + idManager.GetMissingIDString ());
 		}
 
+		// I guess it is for validation error recovery
 		[MonoTODO]
 		public void SkipToEndElement (XmlSchemaInfo info)
 		{
+			CheckState (Transition.Content);
+			if (schemas.Count == 0)
+				return;
 			throw new NotImplementedException ();
 		}
 
 		// I guess this weird XmlValueGetter is for such case that
 		// value might not be required (and thus it improves 
-		// performance in some cases. Doh.
+		// performance in some cases. Doh).
 
+		// The return value is typed primitive, is possible.
 		// AttDeriv
-		[MonoTODO]
 		public object ValidateAttribute (
 			string localName,
 			string ns,
 			XmlValueGetter attributeValue,
 			XmlSchemaInfo info)
 		{
-			throw new NotImplementedException ();
+			CheckState (Transition.StartTag);
+			if (ns == XmlNamespaceManager.XmlnsXmlns)
+				return null;
+			if (ns == XmlSchema.InstanceNamespace) {
+				switch (localName) {
+				case "schemaLocation":
+					HandleSchemaLocation (attributeValue ());
+					break;
+				case "noNamespaceSchemaLocation":
+					HandleNoNSSchemaLocation (attributeValue ());
+					break;
+				case "nil":
+					HandleXsiNil (attributeValue, info);
+					break;
+				case "type":
+					string xsiTypeName = attributeValue ();
+					HandleXsiType (xsiTypeName.Trim (XmlChar.WhitespaceChars));
+					break;
+				default:
+					HandleError ("Unknown schema instance namespace attribute: " + localName);
+					break;
+				}
+				return null; // no further validation.
+			}
+
+			if (schemas.Count == 0)
+				return null;
+
+			occuredAtts.Add (new QName (localName, ns));
+
+			// 3.3.4 Element Locally Valid (Type) - attribute
+			if (Context.ActualType is ComplexType)
+				return AssessAttributeElementLocallyValidType (localName, ns, attributeValue, info);
+			else
+				HandleError ("Current simple type cannot accept attributes other than schema instance namespace.");
+			return null;
 		}
 
 		// StartTagOpenDeriv
-		[MonoTODO]
 		public void ValidateElement (
 			string localName,
 			string ns,
 			XmlSchemaInfo info) // How is it used?
 		{
-			throw new NotImplementedException ();
+			CheckState (Transition.Content);
+			transition = Transition.StartTag;
+			if (schemas.Count == 0)
+				return;
+
+#region ID Constraints
+			if (!IgnoreIdentity)
+				idManager.OnStartElement ();
+#endregion
+			defaultAttributes = emptyAttributeArray;
+
+			elementQNameStack.Add (new XmlQualifiedName (localName, ns));
+
+			// If there is no schema information, then no validation is performed.
+			if (skipValidationDepth < 0 || depth <= skipValidationDepth) {
+				if (shouldValidateCharacters)
+					ValidateEndSimpleContent ();
+
+				AssessOpenStartElementSchemaValidity (localName, ns);
+			}
+
+			shouldValidateCharacters = true;
 		}
 
-		[MonoTODO]
-		public object ValidateEndElement (XmlSchemaInfo scheaInfo)
+		public object ValidateEndElement (XmlSchemaInfo schemaInfo)
 		{
-			throw new NotImplementedException ();
+			return ValidateEndElement (schemaInfo, null);
 		}
 
-		[MonoTODO]
-		public object ValidateEndElement (XmlSchemaInfo scheaInfo,
+		// The return value is typed primitive, is supplied.
+		// EndTagDeriv
+		[MonoTODO ("Find out what 'var' parameter means.")]
+		public object ValidateEndElement (XmlSchemaInfo schemaInfo,
 			object var)
 		{
-			throw new NotImplementedException ();
+			CheckState (Transition.Content);
+			if (schemas.Count == 0)
+				return null;
+			if (depth == 0)
+				throw new InvalidOperationException ("There was no corresponding call to 'ValidateElement' method.");
+
+			depth--;
+
+			object ret = null;
+			if (depth == skipValidationDepth)
+				skipValidationDepth = -1;
+			else if (skipValidationDepth < 0 || depth <= skipValidationDepth)
+				ret = AssessEndElementSchemaValidity ();
+
+			elementQNameStack.RemoveAt (elementQNameStack.Count - 1);
+
+			return ret;
 		}
 
 		// StartTagCloseDeriv
-		[MonoTODO]
 		public void ValidateEndOfAttributes ()
 		{
-			throw new NotImplementedException ();
+			CheckState (Transition.StartTag);
+			transition = Transition.Content;
+			if (schemas.Count == 0)
+				return;
+
+			AssessCloseStartElementSchemaValidity ();
 		}
 
 		// TextDeriv ... without text. Maybe typed check is done by
 		// ValidateAtomicValue().
-		[MonoTODO]
 		public void ValidateText (XmlValueGetter getter)
 		{
-			throw new NotImplementedException ();
+			CheckState (Transition.Content);
+			if (schemas.Count == 0)
+				return;
+
+			ComplexType ct = Context.ActualType as ComplexType;
+			if (ct != null && storedCharacters.Length > 0) {
+				switch (ct.ContentType) {
+				case XmlSchemaContentType.ElementOnly:
+				case XmlSchemaContentType.Empty:
+					HandleError ("Not allowed character content was found.");
+					break;
+				}
+			}
+
+			ValidateCharacters (getter);
 		}
 
+		// TextDeriv...?
 		[MonoTODO]
 		public void ValidateWhitespace (XmlValueGetter getter)
 		{
-			throw new NotImplementedException ();
+			CheckState (Transition.Content);
+			if (schemas.Count == 0)
+				return;
+
+//			throw new NotImplementedException ();
+		}
+
+		#endregion
+
+		#region Error handling
+
+		private void HandleError (string message)
+		{
+			HandleError (message, null, false);
+		}
+
+		private void HandleError (
+			string message, Exception innerException)
+		{
+			HandleError (message, innerException, false);
+		}
+
+		private void HandleError (string message,
+			Exception innerException, bool isWarning)
+		{
+			if (isWarning && IgnoreWarnings)
+				return;
+
+			ValException vex = new ValException (
+				message, nominalEventSender, SourceUri,
+				null, innerException);
+			HandleError (vex, isWarning);
+		}
+
+		private void HandleError (ValException exception)
+		{
+			HandleError (exception, false);
+		}
+
+		private void HandleError (ValException exception, bool isWarning)
+		{
+			if (isWarning && IgnoreWarnings)
+				return;
+
+			if (ValidationEventHandler == null)
+				throw exception;
+
+			ValidationEventArgs e = new ValidationEventArgs (
+				exception,
+				exception.Message,
+				isWarning ? XmlSeverityType.Warning :
+					XmlSeverityType.Error);
+			ValidationEventHandler (nominalEventSender, e);
+		}
+
+		#endregion
+
+		#region External schema resolution
+
+		private XmlSchema ReadExternalSchema (string uri)
+		{
+			Uri absUri = xmlResolver.ResolveUri ((SourceUri != "" ? new Uri (SourceUri) : null), uri);
+			XmlTextReader xtr = null;
+			try {
+				xtr = new XmlTextReader (absUri.ToString (),
+					(Stream) xmlResolver.GetEntity (
+						absUri, null, typeof (Stream)),
+					nameTable);
+				return XmlSchema.Read (
+					xtr, ValidationEventHandler);
+			} finally {
+				if (xtr != null)
+					xtr.Close ();
+			}
+		}
+
+		private void HandleSchemaLocation (string schemaLocation)
+		{
+			if (xmlResolver == null)
+				return;
+			XmlSchema schema = null;
+
+			bool schemaAdded = false;
+			string [] tmp = null;
+			try {
+				schemaLocation = XsDatatype.FromName ("token", XmlSchema.Namespace).Normalize (schemaLocation);
+				tmp = schemaLocation.Split (XmlChar.WhitespaceChars);
+			} catch (Exception ex) {
+				HandleError ("Invalid schemaLocation attribute format.", ex, true);
+				tmp = new string [0];
+			}
+			if (tmp.Length % 2 != 0)
+				HandleError ("Invalid schemaLocation attribute format.");
+			for (int i = 0; i < tmp.Length; i += 2) {
+				try {
+					schema = ReadExternalSchema (tmp [i + 1]);
+				} catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
+					HandleError ("Could not resolve schema location URI: " + schemaLocation, null, true);
+					continue;
+				}
+				if (schema.TargetNamespace == null)
+					schema.TargetNamespace = tmp [i];
+				else if (schema.TargetNamespace != tmp [i])
+					HandleError ("Specified schema has different target namespace.");
+
+				if (schema != null) {
+					if (!schemas.Contains (schema.TargetNamespace)) {
+						schemaAdded = true;
+						schemas.Add (schema);
+					}
+				}
+			}
+			// FIXME: should call Reprocess()?
+			if (schemaAdded)
+				schemas.Compile ();
+		}
+
+		private void HandleNoNSSchemaLocation (string noNsSchemaLocation)
+		{
+			if (xmlResolver == null)
+				return;
+			XmlSchema schema = null;
+			bool schemaAdded = false;
+
+			Uri absUri = null;
+			XmlTextReader xtr = null;
+			try {
+				schema = ReadExternalSchema (noNsSchemaLocation);
+			} catch (Exception) { // FIXME: (wishlist) It is bad manner ;-(
+				HandleError ("Could not resolve schema location URI: " + noNsSchemaLocation, null, true);
+			} finally {
+				if (xtr != null)
+					xtr.Close ();
+			}
+			if (schema != null && schema.TargetNamespace != null)
+				HandleError ("Specified schema has different target namespace.");
+
+			if (schema != null) {
+				if (!schemas.Contains (schema.TargetNamespace)) {
+					schemaAdded = true;
+					schemas.Add (schema);
+				}
+			}
+			// FIXME: should call Reprocess()?
+			if (schemaAdded)
+				schemas.Compile ();
+		}
+
+		#endregion
+
+		private void CheckState (Transition expected)
+		{
+			if (transition != expected) {
+				if (transition == Transition.None)
+					throw new InvalidOperationException ("Initialize() must be called before processing validation.");
+				else
+					throw new InvalidOperationException (
+						String.Format ("Unexpected attempt to validation state transition from {0} to {1} was happened.",
+							transition,
+							expected));
+			}
+		}
+
+		private XsElement FindElement (string name, string ns)
+		{
+			return (XsElement) schemas.GlobalElements [new XmlQualifiedName (name, ns)];
+		}
+
+		private XmlSchemaType FindType (XmlQualifiedName qname)
+		{
+			return (XmlSchemaType) schemas.GlobalTypes [qname];
+		}
+
+		#region Type Validation
+
+		private void ValidateStartElementParticle (
+			string localName, string ns)
+		{
+			if (Context.State == null)
+				return;
+			Context.XsiType = null;
+			state.CurrentElement = null;
+			Context.EvaluateStartElement (localName,
+				ns);
+			if (Context.IsInvalid)
+				HandleError ("Invalid start element: " + ns + ":" + localName);
+
+			Context.SetElement (state.CurrentElement);
+		}
+
+		private void AssessOpenStartElementSchemaValidity (
+			string localName, string ns)
+		{
+			// If the reader is inside xsi:nil (and failed
+			// on validation), then simply skip its content.
+			if (xsiNilDepth >= 0 && xsiNilDepth < depth)
+				HandleError ("Element item appeared, while current element context is nil.");
+
+			ValidateStartElementParticle (localName, ns);
+
+			// Create Validation Root, if not exist.
+			// [Schema Validity Assessment (Element) 1.1]
+			if (Context.Element == null) {
+				state.CurrentElement = FindElement (localName, ns);
+				Context.SetElement (state.CurrentElement);
+			}
+
+#region Key Constraints
+			if (!IgnoreIdentity)
+				ValidateKeySelectors ();
+				ValidateKeyFields (false, xsiNilDepth == depth,
+					Context.ActualType, null, null, null);
+#endregion
+		}
+
+		private void AssessCloseStartElementSchemaValidity ()
+		{
+			if (Context.XsiType != null)
+				AssessCloseStartElementLocallyValidType ();
+			else if (Context.Element != null) {
+				// element locally valid is checked only when
+				// xsi:type does not exist.
+				AssessElementLocallyValidElement ();
+				if (Context.Element.ElementType != null)
+					AssessCloseStartElementLocallyValidType ();
+			}
+
+			if (Context.Element == null) {
+				switch (state.ProcessContents) {
+				case ContentProc.Skip:
+					break;
+				case ContentProc.Lax:
+					break;
+				default:
+					QName current = (QName) elementQNameStack [elementQNameStack.Count - 1];
+					if (Context.XsiType == null &&
+						(schemas.Contains (current.Namespace) ||
+						!schemas.MissedSubComponents (current.Namespace)))
+						HandleError ("Element declaration for " + current + " is missing.");
+					break;
+				}
+			}
+
+			// Proceed to the next depth.
+
+			state.PushContext ();
+
+			XsdValidationState next = null;
+			if (state.ProcessContents == ContentProc.Skip)
+				skipValidationDepth = depth;
+			else {
+				// create child particle state.
+				ComplexType xsComplexType = Context.ActualType as ComplexType;
+				if (xsComplexType != null)
+					next = state.Create (xsComplexType.ValidatableParticle);
+				else if (state.ProcessContents == ContentProc.Lax)
+					next = state.Create (XmlSchemaAny.AnyTypeContent);
+				else
+					next = state.Create (XmlSchemaParticle.Empty);
+			}
+			Context.State = next;
+
+			depth++;
+		}
+
+		// It must be invoked after xsi:nil turned out not to be in
+		// this element.
+		private void AssessElementLocallyValidElement ()
+		{
+			XsElement element = Context.Element;
+			XmlQualifiedName qname = (XmlQualifiedName) elementQNameStack [elementQNameStack.Count - 1];
+			// 1.
+			if (element == null)
+				HandleError ("Element declaration is required for " + qname);
+			// 2.
+			if (element.ActualIsAbstract)
+				HandleError ("Abstract element declaration was specified for " + qname);
+			// 3. is checked inside ValidateAttribute().
+		}
+
+		// 3.3.4 Element Locally Valid (Type)
+		private void AssessCloseStartElementLocallyValidType ()
+		{
+			object schemaType = Context.ActualType;
+			if (schemaType == null) {	// 1.
+				HandleError ("Schema type does not exist.");
+				return;
+			}
+			ComplexType cType = schemaType as ComplexType;
+			SimpleType sType = schemaType as SimpleType;
+			if (sType != null) {
+				// 3.1.1.
+				// Attributes are checked in ValidateAttribute().
+			} else if (cType != null) {
+				// 3.2. Also, 2. is checked there.
+				AssessCloseStartElementLocallyValidComplexType (cType);
+			}
+		}
+
+		// 3.4.4 Element Locally Valid (Complex Type)
+		private void AssessCloseStartElementLocallyValidComplexType (ComplexType cType)
+		{
+			// 1.
+			if (cType.IsAbstract) {
+				HandleError ("Target complex type is abstract.");
+				return;
+			}
+
+			// 2 (xsi:nil and content prohibition)
+			// See AssessStartElementSchemaValidity() and ValidateCharacters()
+			// 3. attribute uses and  5. wild IDs are handled at
+			// ValidateAttribute().
+
+			// Collect default attributes.
+			// 4.
+			foreach (XsAttribute attr in GetExpectedAttributes ()) {
+				if (attr.ValidatedUse == XmlSchemaUse.Required && 
+					attr.ValidatedFixedValue == null)
+					HandleError ("Required attribute " + attr.QualifiedName + " was not found.");
+				else if (attr.ValidatedDefaultValue != null || attr.ValidatedFixedValue != null)
+					defaultAttributesCache.Add (attr);
+			}
+			if (defaultAttributesCache.Count == 0)
+				defaultAttributes = emptyAttributeArray;
+			else
+				defaultAttributes = (XsAttribute []) 
+					defaultAttributesCache.ToArray (
+						typeof (XsAttribute));
+			defaultAttributesCache.Clear ();
+			// 5. wild IDs was already checked at ValidateAttribute().
+
+			occuredAtts.Clear ();
+		}
+
+		private object AssessAttributeElementLocallyValidType (string localName, string ns, XmlValueGetter getter, XmlSchemaInfo info)
+		{
+			ComplexType cType = Context.ActualType as ComplexType;
+			XmlQualifiedName qname = new XmlQualifiedName (localName, ns);
+			// including 3.10.4 Item Valid (Wildcard)
+			XmlSchemaObject attMatch = XmlSchemaUtil.FindAttributeDeclaration (ns, schemas, cType, qname);
+			if (attMatch == null)
+				HandleError ("Attribute declaration was not found for " + qname);
+			XsAttribute attdecl = attMatch as XsAttribute;
+			if (attdecl != null) {
+				AssessAttributeLocallyValidUse (attdecl);
+				return AssessAttributeLocallyValid (attdecl, getter);
+			} // otherwise anyAttribute or null.
+			return null;
+		}
+
+		// 3.2.4 Attribute Locally Valid and 3.4.4
+		private object AssessAttributeLocallyValid (XsAttribute attr, XmlValueGetter getter)
+		{
+			// 2. - 4.
+			if (attr.AttributeType == null)
+				HandleError ("Attribute type is missing for " + attr.QualifiedName);
+			XsDatatype dt = attr.AttributeType as XsDatatype;
+			if (dt == null)
+				dt = ((SimpleType) attr.AttributeType).Datatype;
+			// It is a bit heavy process, so let's omit as long as possible ;-)
+			if (dt != SimpleType.AnySimpleType || attr.ValidatedFixedValue != null) {
+				string normalized = dt.Normalize (getter ());
+				object parsedValue = null;
+				try {
+					parsedValue = dt.ParseValue (normalized, nameTable, nsResolver);
+				} catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-(
+					HandleError ("Attribute value is invalid against its data type " + dt.TokenizedType, ex);
+				}
+				if (attr.ValidatedFixedValue != null && attr.ValidatedFixedValue != normalized) {
+					HandleError ("The value of the attribute " + attr.QualifiedName + " does not match with its fixed value.");
+					parsedValue = dt.ParseValue (attr.ValidatedFixedValue, nameTable, nsResolver);
+				}
+#region ID Constraints
+				if (!IgnoreIdentity) {
+					string error = idManager.AssessEachAttributeIdentityConstraint (dt, parsedValue, ((QName) elementQNameStack [elementQNameStack.Count - 1]).Name);
+					if (error != null)
+						HandleError (error);
+				}
+#endregion
+
+				#region Key Constraints
+				if (!IgnoreIdentity)
+					ValidateKeyFields (
+						true,
+						false,
+						attr.AttributeType,
+						attr.QualifiedName.Name,
+						attr.QualifiedName.Namespace,
+						getter);
+				#endregion
+
+				return parsedValue;
+			}
+			return null;
+		}
+
+		private void AssessAttributeLocallyValidUse (XsAttribute attr)
+		{
+			// This is extra check than spec 3.5.4
+			if (attr.ValidatedUse == XmlSchemaUse.Prohibited)
+				HandleError ("Attribute " + attr.QualifiedName + " is prohibited in this context.");
+		}
+
+		private object AssessEndElementSchemaValidity ()
+		{
+			ValidateEndElementParticle ();	// validate against childrens' state.
+
+			object ret = ValidateEndSimpleContent ();
+
+			// 3.3.4 Assess ElementLocallyValidElement 5: value constraints.
+			// 3.3.4 Assess ElementLocallyValidType 3.1.3. = StringValid(3.14.4)
+			// => ValidateEndSimpleContent ().
+
+#region Key Constraints
+			if (!IgnoreIdentity)
+				ValidateEndElementKeyConstraints ();
+#endregion
+
+			// Reset xsi:nil, if required.
+			if (xsiNilDepth == depth)
+				xsiNilDepth = -1;
+			return ret;
+		}
+
+		private void ValidateEndElementParticle ()
+		{
+			if (Context.State != null) {
+				if (!Context.EvaluateEndElement ()) {
+					HandleError ("Invalid end element. There are still required content items.");
+				}
+			}
+			state.PopContext ();
+		}
+
+		// Utility for missing validation completion related to child items.
+		private void ValidateCharacters (XmlValueGetter getter)
+		{
+			if (xsiNilDepth >= 0 && xsiNilDepth < depth)
+				HandleError ("Element item appeared, while current element context is nil.");
+
+			if (shouldValidateCharacters)
+				storedCharacters.Append (getter ());
+		}
+
+
+		// Utility for missing validation completion related to child items.
+		private object ValidateEndSimpleContent ()
+		{
+			object ret = null;
+			if (shouldValidateCharacters)
+				ret = ValidateEndSimpleContentCore ();
+			shouldValidateCharacters = false;
+			storedCharacters.Length = 0;
+			return ret;
+		}
+
+		private object ValidateEndSimpleContentCore ()
+		{
+			if (Context.ActualType == null)
+				return null;
+
+			string value = storedCharacters.ToString ();
+			object ret = null;
+
+			if (value.Length == 0) {
+				// 3.3.4 Element Locally Valid (Element) 5.1.2
+				if (Context.Element != null) {
+					if (Context.Element.ValidatedDefaultValue != null)
+						value = Context.Element.ValidatedDefaultValue;
+				}					
+			}
+
+			XsDatatype dt = Context.ActualType as XsDatatype;
+			SimpleType st = Context.ActualType as SimpleType;
+			if (dt == null) {
+				if (st != null) {
+					dt = st.Datatype;
+				} else {
+					ComplexType ct = Context.ActualType as ComplexType;
+					dt = ct.Datatype;
+					switch (ct.ContentType) {
+					case XmlSchemaContentType.ElementOnly:
+					case XmlSchemaContentType.Empty:
+						if (value.Length > 0)
+							HandleError ("Character content not allowed.");
+						break;
+					}
+				}
+			}
+			if (dt != null) {
+				// 3.3.4 Element Locally Valid (Element) :: 5.2.2.2. Fixed value constraints
+				if (Context.Element != null && Context.Element.ValidatedFixedValue != null)
+					if (value != Context.Element.ValidatedFixedValue)
+						HandleError ("Fixed value constraint was not satisfied.");
+				ret = AssessStringValid (st, dt, value);
+			}
+
+#region Key Constraints
+			if (!IgnoreIdentity)
+				ValidateSimpleContentIdentity (dt, value);
+#endregion
+
+			shouldValidateCharacters = false;
+			return ret;
+		}
+
+		// 3.14.4 String Valid 
+		private object AssessStringValid (SimpleType st,
+			XsDatatype dt, string value)
+		{
+			XsDatatype validatedDatatype = dt;
+			object ret = null;
+			if (st != null) {
+				string normalized = validatedDatatype.Normalize (value);
+				string [] values;
+				XsDatatype itemDatatype;
+				SimpleType itemSimpleType;
+				switch (st.DerivedBy) {
+				case XmlSchemaDerivationMethod.List:
+					SimpleTypeList listContent = st.Content as SimpleTypeList;
+					values = normalized.Split (XmlChar.WhitespaceChars);
+					// LAMESPEC: Types of each element in
+					// the returned list might be 
+					// inconsistent, so basically returning 
+					// value does not make sense without 
+					// explicit runtime type information 
+					// for base primitive type.
+					object [] retValues = new object [values.Length];
+					itemDatatype = listContent.ValidatedListItemType as XsDatatype;
+					itemSimpleType = listContent.ValidatedListItemType as SimpleType;
+					for (int vi = 0; vi < values.Length; vi++) {
+						string each = values [vi];
+						if (each == String.Empty)
+							continue;
+						// validate against ValidatedItemType
+						if (itemDatatype != null) {
+							try {
+								retValues [vi] = itemDatatype.ParseValue (each, nameTable, nsResolver);
+							} catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-(
+								HandleError ("List type value contains one or more invalid values.", ex);
+								break;
+							}
+						}
+						else
+							AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
+					}
+					ret = retValues;
+					break;
+				case XmlSchemaDerivationMethod.Union:
+					SimpleTypeUnion union = st.Content as SimpleTypeUnion;
+					{
+						string each = normalized;
+						// validate against ValidatedItemType
+						bool passed = false;
+						foreach (object eachType in union.ValidatedTypes) {
+							itemDatatype = eachType as XsDatatype;
+							itemSimpleType = eachType as SimpleType;
+							if (itemDatatype != null) {
+								try {
+									ret = itemDatatype.ParseValue (each, nameTable, nsResolver);
+								} catch (Exception) { // FIXME: (wishlist) better exception handling ;-(
+									continue;
+								}
+							}
+							else {
+								try {
+									ret = AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each);
+								} catch (ValException) {
+									continue;
+								}
+							}
+							passed = true;
+							break;
+						}
+						if (!passed) {
+							HandleError ("Union type value contains one or more invalid values.");
+							break;
+						}
+					}
+					break;
+				case XmlSchemaDerivationMethod.Restriction:
+					SimpleTypeRest str = st.Content as SimpleTypeRest;
+					// facet validation
+					if (str != null) {
+						/* Don't forget to validate against inherited type's facets 
+						 * Could we simplify this by assuming that the basetype will also
+						 * be restriction?
+						 * */
+						 // mmm, will check later.
+						SimpleType baseType = st.BaseXmlSchemaType as SimpleType;
+						if (baseType != null) {
+							 ret = AssessStringValid (baseType, dt, normalized);
+						}
+						if (!str.ValidateValueWithFacets (normalized, nameTable)) {
+							HandleError ("Specified value was invalid against the facets.");
+							break;
+						}
+					}
+					validatedDatatype = st.Datatype;
+					break;
+				}
+			}
+			if (validatedDatatype != null) {
+				try {
+					ret = validatedDatatype.ParseValue (value, nameTable, nsResolver);
+				} catch (Exception ex) {	// FIXME: (wishlist) It is bad manner ;-(
+					HandleError (String.Format ("Invalidly typed data was specified."), ex);
+				}
+			}
+			return ret;
+		}
+
+		#endregion
+
+		#region Key Constraints Validation
+		private XsdKeyTable CreateNewKeyTable (XmlSchemaIdentityConstraint ident)
+		{
+			XsdKeyTable seq = new XsdKeyTable (ident);
+			seq.StartDepth = depth;
+			this.keyTables.Add (seq);
+			return seq;
+		}
+
+		// 3.11.4 Identity Constraint Satisfied
+		private void ValidateKeySelectors ()
+		{
+			if (tmpKeyrefPool != null)
+				tmpKeyrefPool.Clear ();
+			if (Context.Element != null && Context.Element.Constraints.Count > 0) {
+				// (a) Create new key sequences, if required.
+				for (int i = 0; i < Context.Element.Constraints.Count; i++) {
+					XmlSchemaIdentityConstraint ident = (XmlSchemaIdentityConstraint) Context.Element.Constraints [i];
+					XsdKeyTable seq = CreateNewKeyTable (ident);
+					if (ident is XmlSchemaKeyref) {
+						if (tmpKeyrefPool == null)
+							tmpKeyrefPool = new ArrayList ();
+						tmpKeyrefPool.Add (seq);
+					}
+				}
+			}
+
+			// (b) Evaluate current key sequences.
+			for (int i = 0; i < keyTables.Count; i++) {
+				XsdKeyTable seq  = (XsdKeyTable) keyTables [i];
+				if (seq.SelectorMatches (this.elementQNameStack, depth) != null) {
+					// creates and registers new entry.
+					XsdKeyEntry entry = new XsdKeyEntry (seq, depth, lineInfo);
+					seq.Entries.Add (entry);
+				}
+			}
+		}
+
+		private void ValidateKeyFields (bool isAttr, bool isNil, object schemaType, string attrName, string attrNs, XmlValueGetter getter)
+		{
+			// (c) Evaluate field paths.
+			for (int i = 0; i < keyTables.Count; i++) {
+				XsdKeyTable seq  = (XsdKeyTable) keyTables [i];
+				// If possible, create new field entry candidates.
+				for (int j = 0; j < seq.Entries.Count; j++) {
+					try {
+						seq.Entries [j].ProcessMatch (
+							isAttr,
+							elementQNameStack,
+							nominalEventSender,
+							nameTable,
+							SourceUri,
+							schemaType,
+							nsResolver,
+							lineInfo,
+							depth,
+							attrName,
+							attrNs,
+							getter == null ?
+								null :
+								getter (),
+							isNil, 
+							currentKeyFieldConsumers);
+					} catch (ValException ex) {
+						HandleError (ex);
+					}
+				}
+			}
+		}
+
+		private void ProcessKeyEntryOne (XsdKeyEntry entry, bool isAttr, bool isNil, object schemaType, string attrName, string attrNs, XmlValueGetter getter)
+		{
+		}
+
+		private void ValidateEndElementKeyConstraints ()
+		{
+			// Reset Identity constraints.
+			for (int i = 0; i < keyTables.Count; i++) {
+				XsdKeyTable seq = this.keyTables [i] as XsdKeyTable;
+				if (seq.StartDepth == depth) {
+					ValidateEndKeyConstraint (seq);
+				} else {
+					for (int k = 0; k < seq.Entries.Count; k++) {
+						XsdKeyEntry entry = seq.Entries [k] as XsdKeyEntry;
+						// Remove finished (maybe key not found) entries.
+						if (entry.StartDepth == depth) {
+							if (entry.KeyFound)
+								seq.FinishedEntries.Add (entry);
+							else if (seq.SourceSchemaIdentity is XmlSchemaKey)
+								HandleError ("Key sequence is missing.");
+							seq.Entries.RemoveAt (k);
+							k--;
+						}
+						// Pop validated key depth to find two or more fields.
+						else {
+							for (int j = 0; j < entry.KeyFields.Count; j++) {
+								XsdKeyEntryField kf = entry.KeyFields [j];
+								if (!kf.FieldFound && kf.FieldFoundDepth == depth) {
+									kf.FieldFoundDepth = 0;
+									kf.FieldFoundPath = null;
+								}
+							}
+						}
+					}
+				}
+			}
+			for (int i = 0; i < keyTables.Count; i++) {
+				XsdKeyTable seq = this.keyTables [i] as XsdKeyTable;
+				if (seq.StartDepth == depth) {
+					keyTables.RemoveAt (i);
+					i--;
+				}
+			}
+		}
+
+		private void ValidateEndKeyConstraint (XsdKeyTable seq)
+		{
+			ArrayList errors = new ArrayList ();
+			for (int i = 0; i < seq.Entries.Count; i++) {
+				XsdKeyEntry entry = (XsdKeyEntry) seq.Entries [i];
+				if (entry.KeyFound)
+					continue;
+				if (seq.SourceSchemaIdentity is XmlSchemaKey)
+					errors.Add ("line " + entry.SelectorLineNumber + "position " + entry.SelectorLinePosition);
+			}
+			if (errors.Count > 0)
+				HandleError ("Invalid identity constraints were found. Key was not found. "
+					+ String.Join (", ", errors.ToArray (typeof (string)) as string []));
+
+			errors.Clear ();
+			// Find reference target
+			XmlSchemaKeyref xsdKeyref = seq.SourceSchemaIdentity as XmlSchemaKeyref;
+			if (xsdKeyref != null) {
+				for (int i = this.keyTables.Count - 1; i >= 0; i--) {
+					XsdKeyTable target = this.keyTables [i] as XsdKeyTable;
+					if (target.SourceSchemaIdentity == xsdKeyref.Target) {
+						seq.ReferencedKey = target;
+						for (int j = 0; j < seq.FinishedEntries.Count; j++) {
+							XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [j];
+							for (int k = 0; k < target.FinishedEntries.Count; k++) {
+								XsdKeyEntry targetEntry = (XsdKeyEntry) target.FinishedEntries [k];
+								if (entry.CompareIdentity (targetEntry)) {
+									entry.KeyRefFound = true;
+									break;
+								}
+							}
+						}
+					}
+				}
+				if (seq.ReferencedKey == null)
+					HandleError ("Target key was not found.");
+				for (int i = 0; i < seq.FinishedEntries.Count; i++) {
+					XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [i];
+					if (!entry.KeyRefFound)
+						errors.Add (" line " + entry.SelectorLineNumber + ", position " + entry.SelectorLinePosition);
+				}
+				if (errors.Count > 0)
+					HandleError ("Invalid identity constraints were found. Referenced key was not found: "
+						+ String.Join (" / ", errors.ToArray (typeof (string)) as string []));
+			}
+		}
+
+		private void ValidateSimpleContentIdentity (
+			XmlSchemaDatatype dt, string value)
+		{
+			// Identity field value
+			if (currentKeyFieldConsumers != null) {
+				while (this.currentKeyFieldConsumers.Count > 0) {
+					XsdKeyEntryField field = this.currentKeyFieldConsumers [0] as XsdKeyEntryField;
+					if (field.Identity != null)
+						HandleError ("Two or more identical field was found. Former value is '" + field.Identity + "' .");
+					object identity = null; // This means empty value
+					if (dt != null) {
+						try {
+							identity = dt.ParseValue (value, nameTable, nsResolver);
+						} catch (Exception ex) { // FIXME: (wishlist) This is bad manner ;-(
+							HandleError ("Identity value is invalid against its data type " + dt.TokenizedType, ex);
+						}
+					}
+					if (identity == null)
+						identity = value;
+
+					if (!field.SetIdentityField (identity, depth == xsiNilDepth, dt as XsdAnySimpleType, depth, lineInfo))
+						HandleError ("Two or more identical key value was found: '" + value + "' .");
+					this.currentKeyFieldConsumers.RemoveAt (0);
+				}
+			}
+		}
+		#endregion
+
+		#region xsi:type
+		private object GetXsiType (string name)
+		{
+			object xsiType = null;
+			XmlQualifiedName typeQName =
+				XmlQualifiedName.Parse (name, nsResolver);
+			if (typeQName == ComplexType.AnyTypeName)
+				xsiType = ComplexType.AnyType;
+			else if (XmlSchemaUtil.IsBuiltInDatatypeName (typeQName))
+				xsiType = XsDatatype.FromName (typeQName);
+			else
+				xsiType = FindType (typeQName);
+			return xsiType;
+		}
+
+		private void HandleXsiType (string typename)
+		{
+			XsElement element = Context.Element;
+			object xsiType = GetXsiType (typename);
+			if (xsiType == null) {
+				HandleError ("The instance type was not found: " + typename);
+				return;
+			}
+			XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType;
+			if (xsiSchemaType != null && Context.Element != null) {
+				XmlSchemaType elemBaseType = element.ElementType as XmlSchemaType;
+				if (elemBaseType != null && (xsiSchemaType.DerivedBy & elemBaseType.FinalResolved) != 0)
+					HandleError ("The instance type is prohibited by the type of the context element.");
+				if (elemBaseType != xsiType && (xsiSchemaType.DerivedBy & element.BlockResolved) != 0)
+					HandleError ("The instance type is prohibited by the context element.");
+			}
+			ComplexType xsiComplexType = xsiType as ComplexType;
+			if (xsiComplexType != null && xsiComplexType.IsAbstract)
+				HandleError ("The instance type is abstract: " + typename);
+			else {
+				// If current schema type exists, then this xsi:type must be
+				// valid extension of that type. See 1.2.1.2.4.
+				if (element != null) {
+					AssessLocalTypeDerivationOK (xsiType, element.ElementType, element.BlockResolved);
+				}
+				// See also ValidateEndOfAttributes().
+				Context.XsiType = xsiType;
+			}
 		}
 
+		// It is common to ElementLocallyValid::4 and SchemaValidityAssessment::1.2.1.2.4
+		private void AssessLocalTypeDerivationOK (object xsiType, object baseType, XmlSchemaDerivationMethod flag)
+		{
+			XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType;
+			ComplexType baseComplexType = baseType as ComplexType;
+			ComplexType xsiComplexType = xsiSchemaType as ComplexType;
+			if (xsiType != baseType) {
+				// Extracted (not extraneous) check for 3.4.6 TypeDerivationOK.
+				if (baseComplexType != null)
+					flag |= baseComplexType.BlockResolved;
+				if (flag == XmlSchemaDerivationMethod.All) {
+					HandleError ("Prohibited element type substitution.");
+					return;
+				} else if (xsiSchemaType != null && (flag & xsiSchemaType.DerivedBy) != 0) {
+					HandleError ("Prohibited element type substitution.");
+					return;
+				}
+			}
+
+			if (xsiComplexType != null)
+				try {
+					xsiComplexType.ValidateTypeDerivationOK (baseType, null, null);
+				} catch (ValException ex) {
+					HandleError (ex);
+				}
+			else {
+				SimpleType xsiSimpleType = xsiType as SimpleType;
+				if (xsiSimpleType != null) {
+					try {
+						xsiSimpleType.ValidateTypeDerivationOK (baseType, null, null, true);
+					} catch (ValException ex) {
+						HandleError (ex);
+					}
+				}
+				else if (xsiType is XsDatatype) {
+					// do nothing
+				}
+				else
+					HandleError ("Primitive data type cannot be derived type using xsi:type specification.");
+			}
+		}
 		#endregion
+
+		private void HandleXsiNil (XmlValueGetter getter, XmlSchemaInfo info)
+		{
+			XsElement element = Context.Element;
+			if (!element.ActualIsNillable) {
+				HandleError (String.Format ("Current element '{0}' is not nillable and thus does not allow occurence of 'nil' attribute.", Context.Element.QualifiedName));
+				return;
+			}
+			string value = getter ().Trim (XmlChar.WhitespaceChars);
+			// 3.2.
+			// Note that 3.2.1 xsi:nil constraints are to be 
+			// validated in AssessElementSchemaValidity() and 
+			// ValidateCharacters().
+			if (value == "true") {
+				if (element.ValidatedFixedValue != null)
+					HandleError ("Schema instance nil was specified, where the element declaration for " + element.QualifiedName + "has fixed value constraints.");
+				xsiNilDepth = depth;
+				if (info != null)
+					info.IsNil = true;
+			}
+		}
 	}
 }
 

+ 1 - 0
mcs/class/System.XML/System.Xml.dll.sources

@@ -1,5 +1,6 @@
 Assembly/AssemblyInfo.cs
 Assembly/Locale.cs
+Mono.Xml.Schema/XmlSchemaValidatingReader.cs
 Mono.Xml.Schema/XsdIdentityPath.cs
 Mono.Xml.Schema/XsdIdentityState.cs
 Mono.Xml.Schema/XsdKeyTable.cs

+ 7 - 0
mcs/class/System.XML/System.Xml/ChangeLog

@@ -1,3 +1,10 @@
+2004-12-14  Atsushi Enomoto <[email protected]>
+
+	* XmlReaderSettings.cs : XsdValidate and DtdValidate are now
+	  ValidationType.
+	* XmlReader.cs : In Create(), use XmlSchemaValidatingReader for xsd
+	  validation.
+
 2004-12-12  Zoltan Varga  <[email protected]>
 
 	* XmlTextReader.cs: Work around a compiler bug in csc 2.0 beta 1.

+ 12 - 9
mcs/class/System.XML/System.Xml/XmlReader.cs

@@ -281,23 +281,26 @@ namespace System.Xml
 		private static XmlReader CreateValidatingXmlReader (XmlReader reader, XmlReaderSettings settings)
 		{
 			XmlValidatingReader xvr = null;
-			if (settings.DtdValidate) {
+			switch (settings.ValidationType) {
+			case ValidationType.DTD:
 				xvr = new XmlValidatingReader (reader);
-				if (!settings.XsdValidate)
-					xvr.ValidationType = ValidationType.DTD;
-				 // otherwise .Auto by default.
-			} else if (settings.XsdValidate) {
-				xvr = new XmlValidatingReader (reader);
-				xvr.ValidationType = ValidationType.Schema;
+				xvr.ValidationType = ValidationType.DTD;
+				 break;
+			case ValidationType.Schema:
+//				xvr = new XmlValidatingReader (reader);
+//				xvr.ValidationType = ValidationType.Schema;
+				return new Mono.Xml.Schema.XmlSchemaValidatingReader (reader, settings);
+			case ValidationType.XDR:
+				throw new NotSupportedException ();
 			}
 			if (xvr != null)
 				xvr.SetSchemas (settings.Schemas);
 
 			if ((settings.ValidationFlags & XmlSchemaValidationFlags.IgnoreIdentityConstraints) != 0)
 				throw new NotImplementedException ();
-			if ((settings.ValidationFlags & XmlSchemaValidationFlags.IgnoreInlineSchema) == 0)
+			if ((settings.ValidationFlags & XmlSchemaValidationFlags.IgnoreInlineSchema) != 0)
 				throw new NotImplementedException ();
-			if ((settings.ValidationFlags & XmlSchemaValidationFlags.IgnoreSchemaLocation) == 0)
+			if ((settings.ValidationFlags & XmlSchemaValidationFlags.IgnoreSchemaLocation) != 0)
 				throw new NotImplementedException ();
 			if ((settings.ValidationFlags & XmlSchemaValidationFlags.IgnoreValidationWarnings) == 0)
 				throw new NotImplementedException ();

+ 6 - 14
mcs/class/System.XML/System.Xml/XmlReaderSettings.cs

@@ -44,7 +44,6 @@ namespace System.Xml
 		private bool checkCharacters;
 		private bool closeInput;
 		private ConformanceLevel conformance;
-		private bool dtdValidate;
 		private bool ignoreComments;
 		private bool ignoreProcessingInstructions;
 		private bool ignoreWhitespace;
@@ -53,8 +52,8 @@ namespace System.Xml
 		private bool prohibitDtd;
 		private XmlNameTable nameTable;
 		private XmlSchemaSet schemas;
-		private bool xsdValidate;
 		private XsValidationFlags validationFlags;
+		private ValidationType validationType;
 
 		public XmlReaderSettings ()
 		{
@@ -66,7 +65,6 @@ namespace System.Xml
 			checkCharacters = org.checkCharacters;
 			closeInput = org.closeInput;
 			conformance = org.conformance;
-			dtdValidate = org.dtdValidate;
 			ignoreComments = org.ignoreComments;
 			ignoreProcessingInstructions = 
 				org.ignoreProcessingInstructions;
@@ -76,7 +74,7 @@ namespace System.Xml
 			prohibitDtd = org.prohibitDtd;
 			schemas = org.schemas;
 			validationFlags = org.validationFlags;
-			xsdValidate = org.xsdValidate;
+			validationType = org.validationType;
 			nameTable = org.NameTable;
 		}
 
@@ -92,7 +90,6 @@ namespace System.Xml
 			checkCharacters = true;
 			closeInput = false; // ? not documented
 			conformance = ConformanceLevel.Document;
-			dtdValidate = false;
 			ignoreComments = false;
 			ignoreProcessingInstructions = false;
 			ignoreWhitespace = false;
@@ -104,7 +101,7 @@ namespace System.Xml
 				XsValidationFlags.IgnoreValidationWarnings
 				| XsValidationFlags.IgnoreSchemaLocation
 				| XsValidationFlags.IgnoreInlineSchema;
-			xsdValidate = false;
+			validationType = ValidationType.None;
 		}
 
 		public bool CheckCharacters {
@@ -122,11 +119,6 @@ namespace System.Xml
 			set { conformance = value; }
 		}
 
-		public bool DtdValidate {
-			get { return dtdValidate; }
-			set { dtdValidate = value; }
-		}
-
 		public bool IgnoreComments {
 			get { return ignoreComments; }
 			set { ignoreComments = value; }
@@ -178,9 +170,9 @@ namespace System.Xml
 			set { validationFlags = value; }
 		}
 
-		public bool XsdValidate {
-			get { return xsdValidate; }
-			set { xsdValidate = value; }
+		public ValidationType ValidationType {
+			get { return validationType; }
+			set { validationType = value; }
 		}
 	}
 }