X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.XML%2FMono.Xml.Schema%2FXsdValidatingReader.cs;h=97f4a62c363a50ac183f0760dc6ec6686b352d97;hb=73b53a30b0af46d4fc7e29e5e6075f60400e6fa8;hp=195a69c8f89583ca1ee2605f7e2e0f7a927af37f;hpb=33b5f6d8a26b2aed00ccc32ddca6ed1f4d3d6125;p=mono.git diff --git a/mcs/class/System.XML/Mono.Xml.Schema/XsdValidatingReader.cs b/mcs/class/System.XML/Mono.Xml.Schema/XsdValidatingReader.cs index 195a69c8f89..8eea3d30a2a 100644 --- a/mcs/class/System.XML/Mono.Xml.Schema/XsdValidatingReader.cs +++ b/mcs/class/System.XML/Mono.Xml.Schema/XsdValidatingReader.cs @@ -6,96 +6,125 @@ // // (C)2003 Atsushi Enomoto // -// Note: + // -// This class doesn't support set_XmlResolver, since it isn't common to XmlReader interface. -// Try to set that of xml reader which is used to construct this object. +// 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; +#if NET_2_0 +using System.Collections.Generic; +#endif using System.Collections.Specialized; +using System.IO; using System.Text; using System.Xml; using System.Xml.Schema; using Mono.Xml; +#if NET_2_0 +using ValException = System.Xml.Schema.XmlSchemaValidationException; +#else +using ValException = System.Xml.Schema.XmlSchemaException; +#endif + +using QName = System.Xml.XmlQualifiedName; +using ContentProc = System.Xml.Schema.XmlSchemaContentProcessing; +using XsElement = System.Xml.Schema.XmlSchemaElement; +using XsAttribute = System.Xml.Schema.XmlSchemaAttribute; +using ComplexType = System.Xml.Schema.XmlSchemaComplexType; +using SimpleType = System.Xml.Schema.XmlSchemaSimpleType; +using SimpleTypeRest = System.Xml.Schema.XmlSchemaSimpleTypeRestriction; +using SimpleTypeList = System.Xml.Schema.XmlSchemaSimpleTypeList; +using SimpleTypeUnion = System.Xml.Schema.XmlSchemaSimpleTypeUnion; +using XsDatatype = System.Xml.Schema.XmlSchemaDatatype; + namespace Mono.Xml.Schema { - public class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext + internal class XsdValidatingReader : XmlReader, IXmlLineInfo, IHasXmlSchemaInfo, IHasXmlParserContext { - static char [] wsChars = new char [] {' ', '\t', '\n', '\r'}; + static readonly XsAttribute [] emptyAttributeArray = + new XsAttribute [0]; XmlReader reader; - XmlValidatingReader xvReader; XmlResolver resolver; IHasXmlSchemaInfo sourceReaderSchemaInfo; IXmlLineInfo readerLineInfo; - bool laxElementValidation = true; - bool reportNoValidationError; - XmlSchemaCollection schemas = new XmlSchemaCollection (); + ValidationType validationType; + XmlSchemaSet schemas = new XmlSchemaSet (); bool namespaces = true; + bool validationStarted; - Hashtable idList = new Hashtable (); - ArrayList missingIDReferences = new ArrayList (); - string thisElementId; +#region ID Constraints + bool checkIdentity = true; + XsdIDManager idManager = new XsdIDManager (); +#endregion +#region Key Constraints + bool checkKeyConstraints = true; ArrayList keyTables = new ArrayList (); - ArrayList currentKeyFieldConsumers = new ArrayList (); + ArrayList currentKeyFieldConsumers; + ArrayList tmpKeyrefPool; +#endregion + ArrayList elementQNameStack = new ArrayList (); - XsdValidationStateManager stateManager = new XsdValidationStateManager (); - XsdValidationContext context = new XsdValidationContext (); - XsdValidationState childParticleState; + XsdParticleStateManager state = new XsdParticleStateManager (); + int skipValidationDepth = -1; int xsiNilDepth = -1; StringBuilder storedCharacters = new StringBuilder (); bool shouldValidateCharacters; - int skipValidationDepth = -1; - XmlSchemaAttribute [] defaultAttributes = new XmlSchemaAttribute [0]; + XsAttribute [] defaultAttributes = emptyAttributeArray; int currentDefaultAttribute = -1; - XmlQualifiedName currentQName; - - ArrayList elementQNameStack = new ArrayList (); - bool popContext; - - // Property Cache. - int nonDefaultAttributeCount; - bool defaultAttributeConsumed; - - // Validation engine cached object ArrayList defaultAttributesCache = new ArrayList (); - ArrayList tmpKeyrefPool = new ArrayList (); + bool defaultAttributeConsumed; + object currentAttrType; #region .ctor public XsdValidatingReader (XmlReader reader) - : this (reader, null) - { - } - - public XsdValidatingReader (XmlReader reader, XmlReader validatingReader) { this.reader = reader; - xvReader = validatingReader as XmlValidatingReader; - if (xvReader != null) { - if (xvReader.ValidationType == ValidationType.None) - reportNoValidationError = true; - } readerLineInfo = reader as IXmlLineInfo; sourceReaderSchemaInfo = reader as IHasXmlSchemaInfo; + schemas.ValidationEventHandler += ValidationEventHandler; } #endregion - // Provate Properties - private XmlQualifiedName CurrentQName { - get { - if (currentQName == null) - currentQName = new XmlQualifiedName (LocalName, NamespaceURI); - return currentQName; - } + + public ValidationEventHandler ValidationEventHandler; + + // Private Properties + + private XsdValidationContext Context { + get { return state.Context; } } +#region Key Constraints internal ArrayList CurrentKeyFieldConsumers { - get { return currentKeyFieldConsumers; } + get { + if (currentKeyFieldConsumers == null) + currentKeyFieldConsumers = new ArrayList (); + return currentKeyFieldConsumers; + } } +#endregion // Public Non-overrides @@ -108,10 +137,6 @@ namespace Mono.Xml.Schema set { namespaces = value; } } - public XmlReader Reader { - get { return reader; } - } - // This is required to resolve xsi:schemaLocation public XmlResolver XmlResolver { set { @@ -120,8 +145,13 @@ namespace Mono.Xml.Schema } // This should be changed before the first Read() call. - public XmlSchemaCollection Schemas { + public XmlSchemaSet Schemas { get { return schemas; } + set { + if (validationStarted) + throw new InvalidOperationException ("Schemas must be set before the first call to Read()."); + schemas = value; + } } public object SchemaType { @@ -131,20 +161,22 @@ namespace Mono.Xml.Schema switch (NodeType) { case XmlNodeType.Element: - if (context.ActualType != null) - return context.ActualType; - else if (context.Element != null) - return context.Element.ElementType; + if (Context.ActualType != null) + return Context.ActualType; else return SourceReaderSchemaType; case XmlNodeType.Attribute: - XmlSchemaComplexType ct = context.ActualType as XmlSchemaComplexType; - if (ct != null) { - XmlSchemaAttribute attdef = ct.AttributeUses [CurrentQName] as XmlSchemaAttribute; - if (attdef != null) - return attdef.AttributeType; + if (currentAttrType == null) { + ComplexType ct = Context.ActualType as ComplexType; + if (ct != null) { + XsAttribute attdef = ct.AttributeUses [new QName (LocalName, NamespaceURI)] as XsAttribute; + if (attdef != null) + currentAttrType = attdef.AttributeType; + return currentAttrType; + } + currentAttrType = SourceReaderSchemaType; } - return SourceReaderSchemaType; + return currentAttrType; default: return SourceReaderSchemaType; } @@ -155,110 +187,30 @@ namespace Mono.Xml.Schema get { return this.sourceReaderSchemaInfo != null ? sourceReaderSchemaInfo.SchemaType : null; } } - // This property is never used in Mono. public ValidationType ValidationType { - get { - if (reportNoValidationError) - return ValidationType.None; - else - return ValidationType.Schema; + get { return validationType; } + set { + if (validationStarted) + throw new InvalidOperationException ("ValidationType must be set before reading."); + validationType = value; } } // It is used only for independent XmlReader use, not for XmlValidatingReader. public object ReadTypedValue () { - XmlSchemaDatatype dt = SchemaType as XmlSchemaDatatype; - XmlSchemaSimpleType st = SchemaType as XmlSchemaSimpleType; - if (st != null) - dt = st.Datatype; - if (dt == null) - return null; - - switch (NodeType) { - case XmlNodeType.Element: - if (IsEmptyElement) - return null; - - storedCharacters.Length = 0; - bool loop = true; - do { - Read (); - switch (NodeType) { - case XmlNodeType.SignificantWhitespace: - case XmlNodeType.Text: - case XmlNodeType.CDATA: - storedCharacters.Append (Value); - break; - case XmlNodeType.Comment: - break; - default: - loop = false; - break; - } - } while (loop && !EOF); - return dt.ParseValue (storedCharacters.ToString (), NameTable, ParserContext.NamespaceManager); - case XmlNodeType.Attribute: - return dt.ParseValue (Value, NameTable, ParserContext.NamespaceManager); - } - return null; - } - - /* - public ValueType ReadTypedValueType () - { - XmlSchemaDatatype dt = SchemaType as XmlSchemaDatatype; - XmlSchemaSimpleType st = SchemaType as XmlSchemaSimpleType; - if (st != null) - dt = st.Datatype; - if (dt == null) - return null; - - switch (NodeType) { - case XmlNodeType.Element: - if (IsEmptyElement) - return null; - - storedCharacters.Length = 0; - bool loop = true; - do { - Read (); - switch (NodeType) { - case XmlNodeType.SignificantWhitespace: - case XmlNodeType.Text: - case XmlNodeType.CDATA: - storedCharacters.Append (Value); - break; - case XmlNodeType.Comment: - break; - default: - loop = false; - break; - } - } while (loop && !EOF); - return dt.ParseValueType (storedCharacters.ToString (), NameTable, ParserContext.NamespaceManager); - case XmlNodeType.Attribute: - return dt.ParseValueType (Value, NameTable, ParserContext.NamespaceManager); - } - return null; + object o = XmlSchemaUtil.ReadTypedValue (this, + SchemaType, NamespaceManager, + storedCharacters); + storedCharacters.Length = 0; + return o; } - */ - - public ValidationEventHandler ValidationEventHandler; - - // Public Overrided Properties + + // Public Overriden Properties public override int AttributeCount { get { - /* - if (NodeType == XmlNodeType.Element) - return attributeCount; - else if (IsDefault) - return 0; - else - return reader.AttributeCount; - */ - return nonDefaultAttributeCount + defaultAttributes.Length; + return reader.AttributeCount + defaultAttributes.Length; } } @@ -323,11 +275,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; } } @@ -348,7 +300,7 @@ namespace Mono.Xml.Schema if (defaultAttributeConsumed) return String.Empty; - XmlQualifiedName qname = defaultAttributes [currentDefaultAttribute].QualifiedName; + QName qname = defaultAttributes [currentDefaultAttribute].QualifiedName; string prefix = Prefix; if (prefix == String.Empty) return qname.Name; @@ -385,14 +337,18 @@ namespace Mono.Xml.Schema get { return XmlSchemaUtil.GetParserContext (reader); } } + internal XmlNamespaceManager NamespaceManager { + get { return ParserContext != null ? ParserContext.NamespaceManager : null; } + } + 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); + QName qname = defaultAttributes [currentDefaultAttribute].QualifiedName; + string prefix = NamespaceManager != null ? NamespaceManager.LookupPrefix (qname.Namespace, false) : null; if (prefix == null) return String.Empty; else @@ -419,8 +375,6 @@ namespace Mono.Xml.Schema } } - XmlQualifiedName qnameXmlLang = new XmlQualifiedName ("lang", XmlNamespaceManager.XmlnsXml); - public override string XmlLang { get { string xmlLang = reader.XmlLang; @@ -453,16 +407,6 @@ namespace Mono.Xml.Schema // Private Methods - private XmlQualifiedName QualifyName (string name) - { - int colonAt = name.IndexOf (':'); - if (colonAt < 0) - return new XmlQualifiedName (name, null); - else - return new XmlQualifiedName (name.Substring (colonAt + 1), - LookupNamespace (name.Substring (0, colonAt))); - } - private void HandleError (string error) { HandleError (error, null); @@ -475,123 +419,118 @@ namespace Mono.Xml.Schema private void HandleError (string error, Exception innerException, bool isWarning) { - if (reportNoValidationError) // extra quick check + if (ValidationType == ValidationType.None) // extra quick check return; - XmlSchemaException schemaException = new XmlSchemaException (error, + ValException schemaException = new ValException (error, this, this.BaseURI, null, innerException); HandleError (schemaException, isWarning); } - private void HandleError (XmlSchemaException schemaException) + private void HandleError (ValException schemaException) { HandleError (schemaException, false); } - private void HandleError (XmlSchemaException schemaException, bool isWarning) + private void HandleError (ValException schemaException, bool isWarning) { - if (reportNoValidationError) + if (ValidationType == ValidationType.None) return; ValidationEventArgs e = new ValidationEventArgs (schemaException, schemaException.Message, isWarning ? XmlSeverityType.Warning : XmlSeverityType.Error); - if (this.ValidationEventHandler != null) - this.ValidationEventHandler (this, e); - else if (xvReader != null) - xvReader.OnValidationEvent (this, e); + if (ValidationEventHandler != null) + ValidationEventHandler (this, e); + else if (e.Severity == XmlSeverityType.Error) -#if NON_MONO_ENV - this.xvReader.OnValidationEvent (this, e); -#else throw e.Exception; -#endif } - private XmlSchemaElement FindElement (string name, string ns) + private XsElement FindElement (string name, string ns) { - foreach (XmlSchema target in schemas) { - XmlSchema matches = target.Schemas [ns]; - if (matches != null) { - XmlSchemaElement result = target.Elements [new XmlQualifiedName (name, ns)] as XmlSchemaElement; - if (result != null) - return result; - } - } - return null; + return (XsElement) schemas.GlobalElements [new QName (name, ns)]; } - private XmlSchemaType FindType (XmlQualifiedName qname) + private XmlSchemaType FindType (QName qname) { - foreach (XmlSchema target in schemas) { - XmlSchemaType type = target.SchemaTypes [qname] as XmlSchemaType; - if (type != null) - return type; - } - return null; + return (XmlSchemaType) schemas.GlobalTypes [qname]; } private void ValidateStartElementParticle () { - stateManager.CurrentElement = null; - context.ParticleState = context.ParticleState.EvaluateStartElement (reader.LocalName, reader.NamespaceURI); - if (context.ParticleState == XsdValidationState.Invalid) + if (Context.State == null) + return; + Context.XsiType = null; + state.CurrentElement = null; + Context.EvaluateStartElement (reader.LocalName, + reader.NamespaceURI); + if (Context.IsInvalid) HandleError ("Invalid start element: " + reader.NamespaceURI + ":" + reader.LocalName); - context.Element = stateManager.CurrentElement; - if (context.Element != null) - context.SchemaType = context.Element.ElementType; + Context.PushCurrentElement (state.CurrentElement); } private void ValidateEndElementParticle () { - if (childParticleState != null) { - if (!childParticleState.EvaluateEndElement ()) { + if (Context.State != null) { + if (!Context.EvaluateEndElement ()) { HandleError ("Invalid end element: " + reader.Name); } } - context.PopScope (reader.Depth); + Context.PopCurrentElement (); + state.PopContext (); } // Utility for missing validation completion related to child items. private void ValidateCharacters () { - // TODO: value context validation here. if (xsiNilDepth >= 0 && xsiNilDepth < reader.Depth) HandleError ("Element item appeared, while current element context is nil."); - storedCharacters.Append (reader.Value); + if (shouldValidateCharacters) + storedCharacters.Append (reader.Value); } // Utility for missing validation completion related to child items. - private void ValidateEndCharacters () + private void ValidateEndSimpleContent () + { + if (shouldValidateCharacters) + ValidateEndSimpleContentCore (); + shouldValidateCharacters = false; + storedCharacters.Length = 0; + } + + private void ValidateEndSimpleContentCore () { - if (context.ActualType == null) + if (Context.ActualType == null) return; string value = storedCharacters.ToString (); - if (storedCharacters.Length == 0) { + if (value.Length == 0) { // 3.3.4 Element Locally Valid (Element) 5.1.2 - // TODO: check entire DefaultValid (3.3.6) - if (context.Element != null) { - if (context.Element.ValidatedDefaultValue != null) - value = context.Element.ValidatedDefaultValue; + if (Context.Element != null) { + if (Context.Element.ValidatedDefaultValue != null) + value = Context.Element.ValidatedDefaultValue; } } - XmlSchemaDatatype dt = context.ActualType as XmlSchemaDatatype; - XmlSchemaSimpleType st = context.ActualType as XmlSchemaSimpleType; + XsDatatype dt = Context.ActualType as XsDatatype; + SimpleType st = Context.ActualType as SimpleType; if (dt == null) { if (st != null) { dt = st.Datatype; } else { - XmlSchemaComplexType ct = context.ActualType as XmlSchemaComplexType; + ComplexType ct = Context.ActualType as ComplexType; dt = ct.Datatype; switch (ct.ContentType) { case XmlSchemaContentType.ElementOnly: + if (value.Length > 0 && !XmlChar.IsWhitespace (value)) + HandleError ("Character content not allowed."); + break; case XmlSchemaContentType.Empty: - if (storedCharacters.Length > 0) + if (value.Length > 0) HandleError ("Character content not allowed."); break; } @@ -599,59 +538,58 @@ namespace Mono.Xml.Schema } 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) + if (Context.Element != null && Context.Element.ValidatedFixedValue != null) + if (value != Context.Element.ValidatedFixedValue) HandleError ("Fixed value constraint was not satisfied."); AssessStringValid (st, dt, value); } - // Identity field value - 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, ParserContext.NamespaceManager); - } 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, reader.Depth == xsiNilDepth, dt as XsdAnySimpleType, this)) - HandleError ("Two or more identical key value was found: '" + value + "' ."); - this.currentKeyFieldConsumers.RemoveAt (0); - } +#region Key Constraints + if (checkKeyConstraints) + ValidateSimpleContentIdentity (dt, value); +#endregion shouldValidateCharacters = false; } // 3.14.4 String Valid - private void AssessStringValid (XmlSchemaSimpleType st, - XmlSchemaDatatype dt, string value) + private void AssessStringValid (SimpleType st, + XsDatatype dt, string value) { - XmlSchemaDatatype validatedDatatype = dt; + XsDatatype validatedDatatype = dt; if (st != null) { string normalized = validatedDatatype.Normalize (value); + ValidateRestrictedSimpleTypeValue (st, ref validatedDatatype, normalized); + } + if (validatedDatatype != null) { + try { + validatedDatatype.ParseValue (value, NameTable, NamespaceManager); + } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-( + HandleError ("Invalidly typed data was specified.", ex); + } + } + } + + void ValidateRestrictedSimpleTypeValue (SimpleType st, ref XsDatatype dt, string normalized) + { + { string [] values; - XmlSchemaDatatype itemDatatype; - XmlSchemaSimpleType itemSimpleType; + XsDatatype itemDatatype; + SimpleType itemSimpleType; switch (st.DerivedBy) { case XmlSchemaDerivationMethod.List: - XmlSchemaSimpleTypeList listContent = st.Content as XmlSchemaSimpleTypeList; - values = normalized.Split (wsChars); - itemDatatype = listContent.ValidatedListItemType as XmlSchemaDatatype; - itemSimpleType = listContent.ValidatedListItemType as XmlSchemaSimpleType; - foreach (string each in values) { + SimpleTypeList listContent = st.Content as SimpleTypeList; + values = normalized.Split (XmlChar.WhitespaceChars); + 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 { - itemDatatype.ParseValue (each, NameTable, ParserContext.NamespaceManager); + itemDatatype.ParseValue (each, NameTable, NamespaceManager); } catch (Exception ex) { // FIXME: (wishlist) better exception handling ;-( HandleError ("List type value contains one or more invalid values.", ex); break; @@ -662,17 +600,17 @@ namespace Mono.Xml.Schema } break; case XmlSchemaDerivationMethod.Union: - XmlSchemaSimpleTypeUnion union = st.Content as XmlSchemaSimpleTypeUnion; + SimpleTypeUnion union = st.Content as SimpleTypeUnion; { string each = normalized; // validate against ValidatedItemType bool passed = false; foreach (object eachType in union.ValidatedTypes) { - itemDatatype = eachType as XmlSchemaDatatype; - itemSimpleType = eachType as XmlSchemaSimpleType; + itemDatatype = eachType as XsDatatype; + itemSimpleType = eachType as SimpleType; if (itemDatatype != null) { try { - itemDatatype.ParseValue (each, NameTable, ParserContext.NamespaceManager); + itemDatatype.ParseValue (each, NameTable, NamespaceManager); } catch (Exception) { // FIXME: (wishlist) better exception handling ;-( continue; } @@ -680,7 +618,7 @@ namespace Mono.Xml.Schema else { try { AssessStringValid (itemSimpleType, itemSimpleType.Datatype, each); - } catch (XmlSchemaException) { + } catch (ValException) { continue; } } @@ -694,7 +632,7 @@ namespace Mono.Xml.Schema } break; case XmlSchemaDerivationMethod.Restriction: - XmlSchemaSimpleTypeRestriction str = st.Content as XmlSchemaSimpleTypeRestriction; + SimpleTypeRest str = st.Content as SimpleTypeRest; // facet validation if (str != null) { /* Don't forget to validate against inherited type's facets @@ -702,36 +640,29 @@ namespace Mono.Xml.Schema * be restriction? * */ // mmm, will check later. - XmlSchemaSimpleType baseType = st.BaseXmlSchemaType as XmlSchemaSimpleType; + SimpleType baseType = st.BaseXmlSchemaType as SimpleType; if (baseType != null) { AssessStringValid(baseType, dt, normalized); } - if (!str.ValidateValueWithFacets (normalized, NameTable)) { + if (!str.ValidateValueWithFacets (normalized, NameTable, NamespaceManager)) { HandleError ("Specified value was invalid against the facets."); break; } } - validatedDatatype = st.Datatype; + dt = st.Datatype; break; } } - if (validatedDatatype != null) { - try { - validatedDatatype.ParseValue (value, NameTable, ParserContext.NamespaceManager); - } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-( - HandleError ("Invalidly typed data was specified.", ex); - } - } } - private object GetLocalTypeDefinition (string name) + private object GetXsiType (string name) { object xsiType = null; - XmlQualifiedName typeQName = QualifyName (name); - if (typeQName == XmlSchemaComplexType.AnyTypeName) - xsiType = XmlSchemaComplexType.AnyType; + QName typeQName = QName.Parse (name, this); + if (typeQName == ComplexType.AnyTypeName) + xsiType = ComplexType.AnyType; else if (XmlSchemaUtil.IsBuiltInDatatypeName (typeQName)) - xsiType = XmlSchemaDatatype.FromName (typeQName); + xsiType = XsDatatype.FromName (typeQName); else xsiType = FindType (typeQName); return xsiType; @@ -741,8 +672,8 @@ namespace Mono.Xml.Schema private void AssessLocalTypeDerivationOK (object xsiType, object baseType, XmlSchemaDerivationMethod flag) { XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType; - XmlSchemaComplexType baseComplexType = baseType as XmlSchemaComplexType; - XmlSchemaComplexType xsiComplexType = xsiSchemaType as XmlSchemaComplexType; + 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) @@ -759,21 +690,21 @@ namespace Mono.Xml.Schema if (xsiComplexType != null) try { xsiComplexType.ValidateTypeDerivationOK (baseType, null, null); - } catch (XmlSchemaException ex) { + } catch (ValException ex) { // HandleError ("Locally specified schema complex type derivation failed. " + ex.Message, ex); HandleError (ex); } else { - XmlSchemaSimpleType xsiSimpleType = xsiType as XmlSchemaSimpleType; + SimpleType xsiSimpleType = xsiType as SimpleType; if (xsiSimpleType != null) { try { xsiSimpleType.ValidateTypeDerivationOK (baseType, null, null, true); - } catch (XmlSchemaException ex) { + } catch (ValException ex) { // HandleError ("Locally specified schema simple type derivation failed. " + ex.Message, ex); HandleError (ex); } } - else if (xsiType is XmlSchemaDatatype) { + else if (xsiType is XsDatatype) { // do nothing } else @@ -784,22 +715,12 @@ namespace Mono.Xml.Schema // Section 3.3.4 of the spec. private void AssessStartElementSchemaValidity () { - // If the reader is inside xsi:nil (and failed on validation), - // then simply skip its content. + // If the reader is inside xsi:nil (and failed + // on validation), then simply skip its content. if (xsiNilDepth >= 0 && xsiNilDepth < reader.Depth) HandleError ("Element item appeared, while current element context is nil."); - context.Load (reader.Depth); - if (childParticleState != null) { - context.ParticleState = childParticleState; - childParticleState = null; - } - - // If validation state exists, then first assess particle validity. - context.SchemaType = null; - if (context.ParticleState != null) { - ValidateStartElementParticle (); - } + ValidateStartElementParticle (); string xsiNilValue = reader.GetAttribute ("nil", XmlSchema.InstanceNamespace); if (xsiNilValue != null) @@ -817,86 +738,89 @@ namespace Mono.Xml.Schema string xsiTypeName = reader.GetAttribute ("type", XmlSchema.InstanceNamespace); if (xsiTypeName != null) { xsiTypeName = xsiTypeName.Trim (XmlChar.WhitespaceChars); - object xsiType = GetLocalTypeDefinition (xsiTypeName); + object xsiType = GetXsiType (xsiTypeName); if (xsiType == null) HandleError ("The instance type was not found: " + xsiTypeName + " ."); else { XmlSchemaType xsiSchemaType = xsiType as XmlSchemaType; - if (xsiSchemaType != null && this.context.Element != null) { - XmlSchemaType elemBaseType = context.Element.ElementType as XmlSchemaType; + if (xsiSchemaType != null && this.Context.Element != null) { + XmlSchemaType elemBaseType = Context.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 & this.context.Element.BlockResolved) != 0) + if (elemBaseType != xsiType && (xsiSchemaType.DerivedBy & this.Context.Element.BlockResolved) != 0) HandleError ("The instance type is prohibited by the context element."); } - XmlSchemaComplexType xsiComplexType = xsiType as XmlSchemaComplexType; + ComplexType xsiComplexType = xsiType as ComplexType; if (xsiComplexType != null && xsiComplexType.IsAbstract) HandleError ("The instance type is abstract: " + xsiTypeName + " ."); else { // If current schema type exists, then this xsi:type must be // valid extension of that type. See 1.2.1.2.4. - if (context.Element != null) { - // FIXME: supply *correct* base type - AssessLocalTypeDerivationOK (xsiType, context.Element.ElementType, context.Element.BlockResolved); + if (Context.Element != null) { + AssessLocalTypeDerivationOK (xsiType, Context.Element.ElementType, Context.Element.BlockResolved); } AssessStartElementLocallyValidType (xsiType); // 1.2.2: - context.LocalTypeDefinition = xsiType; + Context.XsiType = xsiType; } } } - else - context.LocalTypeDefinition = null; // Create Validation Root, if not exist. // [Schema Validity Assessment (Element) 1.1] - if (context.Element == null) - context.Element = FindElement (reader.LocalName, reader.NamespaceURI); - if (context.Element != null) { - if (xsiTypeName == null) { - context.SchemaType = context.Element.ElementType; - AssessElementLocallyValidElement (context.Element, xsiNilValue); // 1.1.2 + if (Context.Element == null) { + state.CurrentElement = FindElement (reader.LocalName, reader.NamespaceURI); + Context.PushCurrentElement (state.CurrentElement); + } + if (Context.Element != null) { + if (Context.XsiType == null) { + AssessElementLocallyValidElement (xsiNilValue); // 1.1.2 } } else { - XmlSchema schema; - switch (stateManager.ProcessContents) { - case XmlSchemaContentProcessing.Skip: + switch (state.ProcessContents) { + case ContentProc.Skip: break; - case XmlSchemaContentProcessing.Lax: - /* - schema = schemas [reader.NamespaceURI]; - if (schema != null && !schema.missedSubComponents) - HandleError ("Element declaration for " + reader.LocalName + " is missing."); - */ + case ContentProc.Lax: break; default: - schema = schemas [reader.NamespaceURI]; - if (xsiTypeName == null && (schema == null || !schema.missedSubComponents)) - HandleError ("Element declaration for " + reader.LocalName + " is missing."); + if (xsiTypeName == null && + (schemas.Contains (reader.NamespaceURI) || + !schemas.MissedSubComponents (reader.NamespaceURI))) + HandleError ("Element declaration for " + new QName (reader.LocalName, reader.NamespaceURI) + " is missing."); break; } } - if (stateManager.ProcessContents == XmlSchemaContentProcessing.Skip) - skipValidationDepth = reader.Depth; + state.PushContext (); - // Finally, create child particle state. - XmlSchemaComplexType xsComplexType = SchemaType as XmlSchemaComplexType; - if (xsComplexType != null) - childParticleState = stateManager.Create (xsComplexType.ValidatableParticle); - else if (stateManager.ProcessContents == XmlSchemaContentProcessing.Lax) - childParticleState = stateManager.Create (XmlSchemaAny.AnyTypeContent); - else - childParticleState = stateManager.Create (XmlSchemaParticle.Empty); + XsdValidationState next = null; + if (state.ProcessContents == ContentProc.Skip) + skipValidationDepth = reader.Depth; + else { + // create child particle state. + ComplexType xsComplexType = SchemaType 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; - AssessStartIdentityConstraints (); +#region Key Constraints + if (checkKeyConstraints) { + ValidateKeySelectors (); + ValidateKeyFields (); + } +#endregion - context.PushScope (reader.Depth); } // 3.3.4 Element Locally Valid (Element) - private void AssessElementLocallyValidElement (XmlSchemaElement element, string xsiNilValue) + private void AssessElementLocallyValidElement (string xsiNilValue) { - XmlQualifiedName qname = new XmlQualifiedName (reader.LocalName, reader.NamespaceURI); + XsElement element = Context.Element; + QName qname = new QName (reader.LocalName, reader.NamespaceURI); // 1. if (element == null) HandleError ("Element declaration is required for " + qname); @@ -907,21 +831,21 @@ namespace Mono.Xml.Schema if (!element.ActualIsNillable && xsiNilValue != null) HandleError ("This element declaration is not nillable: " + qname); // 3.2. - // Note that 3.2.1 xsi:nil constraints are to be validated in + // Note that 3.2.1 xsi:nil constraints are to be + // validated in AssessElementSchemaValidity() and + // ValidateCharacters(). else if (xsiNilValue == "true") { - // AssessElementSchemaValidity() and ValidateCharacters() - if (element.ValidatedFixedValue != null) HandleError ("Schema instance nil was specified, where the element declaration for " + qname + "has fixed value constraints."); } - // 4. + // 4. xsi:type (it takes precedence than element type) string xsiType = reader.GetAttribute ("type", XmlSchema.InstanceNamespace); if (xsiType != null) { - context.LocalTypeDefinition = GetLocalTypeDefinition (xsiType); - AssessLocalTypeDerivationOK (context.LocalTypeDefinition, element.ElementType, element.BlockResolved); + Context.XsiType = GetXsiType (xsiType); + AssessLocalTypeDerivationOK (Context.XsiType, element.ElementType, element.BlockResolved); } else - context.LocalTypeDefinition = null; + Context.XsiType = null; // 5 Not all things cannot be assessed here. // It is common to 5.1 and 5.2 @@ -941,8 +865,8 @@ namespace Mono.Xml.Schema HandleError ("Schema type does not exist."); return; } - XmlSchemaComplexType cType = schemaType as XmlSchemaComplexType; - XmlSchemaSimpleType sType = schemaType as XmlSchemaSimpleType; + ComplexType cType = schemaType as ComplexType; + SimpleType sType = schemaType as SimpleType; if (sType != null) { // 3.1.1. while (reader.MoveToNextAttribute ()) { @@ -974,8 +898,7 @@ namespace Mono.Xml.Schema } // 3.4.4 Element Locally Valid (Complex Type) - // TODO ("wild IDs constraints.") - private void AssessElementLocallyValidComplexType (XmlSchemaComplexType cType) + private void AssessElementLocallyValidComplexType (ComplexType cType) { // 1. if (cType.IsAbstract) @@ -984,180 +907,93 @@ namespace Mono.Xml.Schema // 2 (xsi:nil and content prohibition) // See AssessStartElementSchemaValidity() and ValidateCharacters() - string elementNs = reader.NamespaceURI; // 3. attribute uses and // 5. wild IDs - while (reader.MoveToNextAttribute ()) { - if (reader.NamespaceURI == "http://www.w3.org/2000/xmlns/") - continue; - else if (reader.NamespaceURI == XmlSchema.InstanceNamespace) - continue; - XmlQualifiedName qname = new XmlQualifiedName (reader.LocalName, reader.NamespaceURI); - object attMatch = FindAttributeDeclaration (cType, qname, elementNs); - if (attMatch == null) - HandleError ("Attribute declaration was not found for " + qname); - else { - XmlSchemaAttribute attdecl = attMatch as XmlSchemaAttribute; - if (attdecl == null) { // i.e. anyAttribute - XmlSchemaAnyAttribute anyAttrMatch = attMatch as XmlSchemaAnyAttribute; - } else { - AssessAttributeLocallyValidUse (attdecl); - AssessAttributeLocallyValid (attdecl, true); + if (reader.MoveToFirstAttribute ()) { + do { + switch (reader.NamespaceURI) { + case"http://www.w3.org/2000/xmlns/": + case XmlSchema.InstanceNamespace: + continue; } - } + QName qname = new QName (reader.LocalName, reader.NamespaceURI); + // including 3.10.4 Item Valid (Wildcard) + XmlSchemaObject attMatch = XmlSchemaUtil.FindAttributeDeclaration (reader.NamespaceURI, schemas, cType, qname); + if (attMatch == null) + HandleError ("Attribute declaration was not found for " + qname); + XsAttribute attdecl = attMatch as XsAttribute; + if (attdecl != null) { + AssessAttributeLocallyValidUse (attdecl); + AssessAttributeLocallyValid (attdecl); + } // otherwise anyAttribute or null. + } while (reader.MoveToNextAttribute ()); + reader.MoveToElement (); } - reader.MoveToElement (); // Collect default attributes. // 4. - // FIXME: FixedValue check maybe extraneous. foreach (DictionaryEntry entry in cType.AttributeUses) { - XmlSchemaAttribute attr = (XmlSchemaAttribute) entry.Value; + XsAttribute attr = (XsAttribute) entry.Value; if (reader [attr.QualifiedName.Name, attr.QualifiedName.Namespace] == null) { if (attr.ValidatedUse == XmlSchemaUse.Required && attr.ValidatedFixedValue == null) HandleError ("Required attribute " + attr.QualifiedName + " was not found."); - else if (attr.ValidatedDefaultValue != null) - defaultAttributesCache.Add (attr); - else if (attr.ValidatedFixedValue != null) + else if (attr.ValidatedDefaultValue != null || attr.ValidatedFixedValue != null) defaultAttributesCache.Add (attr); } } - defaultAttributes = (XmlSchemaAttribute []) - defaultAttributesCache.ToArray (typeof (XmlSchemaAttribute)); - context.DefaultAttributes = defaultAttributes; + if (defaultAttributesCache.Count == 0) + defaultAttributes = emptyAttributeArray; + else + defaultAttributes = (XsAttribute []) + defaultAttributesCache.ToArray ( + typeof (XsAttribute)); defaultAttributesCache.Clear (); // 5. wild IDs was already checked above. } - // Spec 3.10.4 Item Valid (Wildcard) - private bool AttributeWildcardItemValid (XmlSchemaAnyAttribute anyAttr, XmlQualifiedName qname) - { - if (anyAttr.HasValueAny) - return true; - if (anyAttr.HasValueOther && (anyAttr.TargetNamespace == "" || reader.NamespaceURI != anyAttr.TargetNamespace)) - return true; - if (anyAttr.HasValueTargetNamespace && reader.NamespaceURI == anyAttr.TargetNamespace) - return true; - if (anyAttr.HasValueLocal && reader.NamespaceURI == "") - return true; - foreach (string ns in anyAttr.ResolvedNamespaces) - if (ns == reader.NamespaceURI) - return true; - return false; - } - - private XmlSchemaObject FindAttributeDeclaration (XmlSchemaComplexType cType, - XmlQualifiedName qname, string elementNs) - { - XmlSchemaObject result = cType.AttributeUses [qname]; - if (result != null) - return result; - if (cType.AttributeWildcard == null) - return null; - - if (!AttributeWildcardItemValid (cType.AttributeWildcard, qname)) - return null; - - if (cType.AttributeWildcard.ResolvedProcessContents == XmlSchemaContentProcessing.Skip) - return cType.AttributeWildcard; - foreach (XmlSchema schema in schemas) { - foreach (DictionaryEntry entry in schema.Attributes) { - XmlSchemaAttribute attr = (XmlSchemaAttribute) entry.Value; - if (attr.QualifiedName == qname) - return attr; - } - } - if (cType.AttributeWildcard.ResolvedProcessContents == XmlSchemaContentProcessing.Lax) - return cType.AttributeWildcard; - else - return null; - } - - // 3.2.4 Attribute Locally Valid and 3.4.4 - 5.wildIDs - // TODO - private void AssessAttributeLocallyValid (XmlSchemaAttribute attr, bool checkWildIDs) + // 3.2.4 Attribute Locally Valid and 3.4.4 + private void AssessAttributeLocallyValid (XsAttribute attr) { - // 1. - switch (reader.NamespaceURI) { - case XmlNamespaceManager.XmlnsXml: - case XmlNamespaceManager.XmlnsXmlns: - case XmlSchema.InstanceNamespace: - break; - } - // TODO 2. - 4. + // 2. - 4. if (attr.AttributeType == null) HandleError ("Attribute type is missing for " + attr.QualifiedName); - XmlSchemaDatatype dt = attr.AttributeType as XmlSchemaDatatype; + XsDatatype dt = attr.AttributeType as XsDatatype; if (dt == null) - dt = ((XmlSchemaSimpleType) attr.AttributeType).Datatype; + dt = ((SimpleType) attr.AttributeType).Datatype; // It is a bit heavy process, so let's omit as long as possible ;-) - if (dt != XmlSchemaSimpleType.AnySimpleType || attr.ValidatedFixedValue != null) { + if (dt != SimpleType.AnySimpleType || attr.ValidatedFixedValue != null) { string normalized = dt.Normalize (reader.Value); object parsedValue = null; + + // check part of 3.14.4 StringValid + SimpleType st = attr.AttributeType as SimpleType; + if (st != null) + ValidateRestrictedSimpleTypeValue (st, ref dt, normalized); + try { - parsedValue = dt.ParseValue (normalized, reader.NameTable, this.ParserContext.NamespaceManager); + parsedValue = dt.ParseValue (normalized, reader.NameTable, NamespaceManager); } 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."); - // FIXME: this is extraneous checks in 3.2.4 Attribute Locally Valid. - if (checkWildIDs) - AssessEachAttributeIdentityConstraint (dt, normalized, parsedValue); - } - } - private void AssessEachAttributeIdentityConstraint (XmlSchemaDatatype dt, - string normalized, object parsedValue) - { - // Get normalized value and (if required) parsedValue if missing. - switch (dt.TokenizedType) { - case XmlTokenizedType.IDREFS: - if (normalized == null) - normalized = dt.Normalize (reader.Value); - if (parsedValue == null) - parsedValue = dt.ParseValue (normalized, reader.NameTable, ParserContext.NamespaceManager); - break; - case XmlTokenizedType.ID: - case XmlTokenizedType.IDREF: - if (normalized == null) - normalized = dt.Normalize (reader.Value); - break; - } - - // Validate identity constraints. - switch (dt.TokenizedType) { - case XmlTokenizedType.ID: - if (thisElementId != null) - HandleError ("ID type attribute was already assigned in the containing element."); - thisElementId = normalized; - if (idList.Contains (normalized)) - HandleError ("Duplicate ID value was found."); - else - idList.Add (normalized, normalized); - break; - case XmlTokenizedType.IDREF: - if (missingIDReferences.Contains (normalized)) - missingIDReferences.Remove (normalized); - else - missingIDReferences.Add (normalized); - break; - case XmlTokenizedType.IDREFS: - foreach (string id in (string []) parsedValue) { - if (missingIDReferences.Contains (id)) - missingIDReferences.Remove (id); - else - missingIDReferences.Add (id); + 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, reader.NameTable, NamespaceManager); } - break; +#region ID Constraints + if (this.checkIdentity) { + string error = idManager.AssessEachAttributeIdentityConstraint (dt, parsedValue, ((QName) elementQNameStack [elementQNameStack.Count - 1]).Name); + if (error != null) + HandleError (error); + } +#endregion } } - // TODO - private void AssessAttributeLocallyValidUse (XmlSchemaAttribute attr) + private void AssessAttributeLocallyValidUse (XsAttribute attr) { - // TODO: value constraint check // This is extra check than spec 3.5.4 if (attr.ValidatedUse == XmlSchemaUse.Prohibited) HandleError ("Attribute " + attr.QualifiedName + " is prohibited in this context."); @@ -1165,39 +1001,48 @@ namespace Mono.Xml.Schema private void AssessEndElementSchemaValidity () { - if (childParticleState == null) - childParticleState = context.ParticleState; - ValidateEndElementParticle (); // validate against childrens' state. + ValidateEndSimpleContent (); - if (shouldValidateCharacters) { - ValidateEndCharacters (); - shouldValidateCharacters = false; - } + ValidateEndElementParticle (); // validate against childrens' state. // 3.3.4 Assess ElementLocallyValidElement 5: value constraints. // 3.3.4 Assess ElementLocallyValidType 3.1.3. = StringValid(3.14.4) - // => ValidateEndCharacters(). + // => ValidateEndSimpleContent(). + +#region Key Constraints + if (checkKeyConstraints) + ValidateEndElementKeyConstraints (); +#endregion + + // Reset xsi:nil, if required. + if (xsiNilDepth == reader.Depth) + xsiNilDepth = -1; + } +#region Key Constraints + private void ValidateEndElementKeyConstraints () + { // Reset Identity constraints. for (int i = 0; i < keyTables.Count; i++) { - XsdKeyTable keyTable = this.keyTables [i] as XsdKeyTable; - if (keyTable.StartDepth == reader.Depth) { - EndIdentityValidation (keyTable); + XsdKeyTable seq = this.keyTables [i] as XsdKeyTable; + if (seq.StartDepth == reader.Depth) { + EndIdentityValidation (seq); } else { - for (int k = 0; k < keyTable.Entries.Count; k++) { - XsdKeyEntry entry = keyTable.Entries [k] as XsdKeyEntry; + 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 == reader.Depth) { if (entry.KeyFound) - keyTable.FinishedEntries.Add (entry); - else if (entry.KeySequence.SourceSchemaIdentity is XmlSchemaKey) + seq.FinishedEntries.Add (entry); + else if (seq.SourceSchemaIdentity is XmlSchemaKey) HandleError ("Key sequence is missing."); - keyTable.Entries.RemoveAt (k); + seq.Entries.RemoveAt (k); k--; } // Pop validated key depth to find two or more fields. else { - foreach (XsdKeyEntryField kf in entry.KeyFields) { + for (int j = 0; j < entry.KeyFields.Count; j++) { + XsdKeyEntryField kf = entry.KeyFields [j]; if (!kf.FieldFound && kf.FieldFoundDepth == reader.Depth) { kf.FieldFoundDepth = 0; kf.FieldFoundPath = null; @@ -1208,109 +1053,180 @@ namespace Mono.Xml.Schema } } for (int i = 0; i < keyTables.Count; i++) { - XsdKeyTable keyseq = this.keyTables [i] as XsdKeyTable; - if (keyseq.StartDepth == reader.Depth) { + XsdKeyTable seq = this.keyTables [i] as XsdKeyTable; + if (seq.StartDepth == reader.Depth) { keyTables.RemoveAt (i); i--; } } - - // Reset xsi:nil, if required. - if (xsiNilDepth == reader.Depth) - xsiNilDepth = -1; } // 3.11.4 Identity Constraint Satisfied - // TODO - private void AssessStartIdentityConstraints () + private void ValidateKeySelectors () { - tmpKeyrefPool.Clear (); - if (context.Element != null && context.Element.Constraints.Count > 0) { + if (tmpKeyrefPool != null) + tmpKeyrefPool.Clear (); + if (Context.Element != null && Context.Element.Constraints.Count > 0) { // (a) Create new key sequences, if required. - foreach (XmlSchemaIdentityConstraint ident in context.Element.Constraints) { + 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 (ident is XmlSchemaKeyref) { + if (tmpKeyrefPool == null) + tmpKeyrefPool = new ArrayList (); tmpKeyrefPool.Add (seq); + } } } // (b) Evaluate current key sequences. - foreach (XsdKeyTable seq in this.keyTables) { - if (seq.SelectorMatches (this.elementQNameStack, reader) != null) { + for (int i = 0; i < keyTables.Count; i++) { + XsdKeyTable seq = (XsdKeyTable) keyTables [i]; + if (seq.SelectorMatches (this.elementQNameStack, reader.Depth) != null) { // creates and registers new entry. - XsdKeyEntry entry = new XsdKeyEntry (seq, reader); + XsdKeyEntry entry = new XsdKeyEntry (seq, reader.Depth, readerLineInfo); seq.Entries.Add (entry); } } + } + private void ValidateKeyFields () + { // (c) Evaluate field paths. - foreach (XsdKeyTable seq in this.keyTables) { + for (int i = 0; i < keyTables.Count; i++) { + XsdKeyTable seq = (XsdKeyTable) keyTables [i]; // If possible, create new field entry candidates. - for (int i = 0; i < seq.Entries.Count; i++) { - XsdKeyEntry entry = seq.Entries [i] as XsdKeyEntry; -// if (entry.KeyFound) -// FIXME: it should not be skipped for multiple key check!! -// continue; + for (int j = 0; j < seq.Entries.Count; j++) { try { - entry.FieldMatches (this.elementQNameStack, this); - } catch (Exception ex) { // FIXME: (wishlist) It is bad manner ;-( - HandleError ("Identity field value is invalid against its data type.", ex); + ProcessKeyEntry (seq.Entries [j]); + } catch (ValException ex) { + HandleError (ex); } } } } + private void ProcessKeyEntry (XsdKeyEntry entry) + { + bool isNil = XsiNilDepth == Depth; + entry.ProcessMatch (false, elementQNameStack, this, NameTable, BaseURI, SchemaType, NamespaceManager, readerLineInfo, Depth, null, null, null, isNil, CurrentKeyFieldConsumers); + if (MoveToFirstAttribute ()) { + try { + do { + switch (NamespaceURI) { + case XmlNamespaceManager.XmlnsXmlns: + case XmlSchema.InstanceNamespace: + continue; + } + XmlSchemaDatatype dt = SchemaType as XmlSchemaDatatype; + XmlSchemaSimpleType st = SchemaType as XmlSchemaSimpleType; + if (dt == null && st != null) + dt = st.Datatype; + object identity = null; + if (dt != null) + identity = dt.ParseValue (Value, NameTable, NamespaceManager); + if (identity == null) + identity = Value; + entry.ProcessMatch (true, elementQNameStack, this, NameTable, BaseURI, SchemaType, NamespaceManager, readerLineInfo, Depth, LocalName, NamespaceURI, identity, false, CurrentKeyFieldConsumers); + } while (MoveToNextAttribute ()); + } finally { + MoveToElement (); + } + } + } + private XsdKeyTable CreateNewKeyTable (XmlSchemaIdentityConstraint ident) { - XsdKeyTable seq = new XsdKeyTable (ident, this); + XsdKeyTable seq = new XsdKeyTable (ident); seq.StartDepth = reader.Depth; - XmlSchemaKeyref keyref = ident as XmlSchemaKeyref; this.keyTables.Add (seq); return seq; } + 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, NamespaceManager); + } 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, reader.Depth == xsiNilDepth, dt as XsdAnySimpleType, this.Depth, readerLineInfo)) + HandleError ("Two or more identical key value was found: '" + value + "' ."); + this.currentKeyFieldConsumers.RemoveAt (0); + } + } + } + private void EndIdentityValidation (XsdKeyTable seq) { - ArrayList errors = new ArrayList (); - foreach (XsdKeyEntry entry in seq./*NotFound*/Entries) { + ArrayList errors = null; + for (int i = 0; i < seq.Entries.Count; i++) { + XsdKeyEntry entry = (XsdKeyEntry) seq.Entries [i]; if (entry.KeyFound) continue; - if (seq.SourceSchemaIdentity is XmlSchemaKey) + if (seq.SourceSchemaIdentity is XmlSchemaKey) { + if (errors == null) + errors = new ArrayList (); errors.Add ("line " + entry.SelectorLineNumber + "position " + entry.SelectorLinePosition); + } } - if (errors.Count > 0) + if (errors != null) HandleError ("Invalid identity constraints were found. Key was not found. " + String.Join (", ", errors.ToArray (typeof (string)) as string [])); - errors.Clear (); - // Find reference target + // If it is keyref, then 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; - foreach (XsdKeyEntry entry in seq.FinishedEntries) { - foreach (XsdKeyEntry targetEntry in target.FinishedEntries) { - if (entry.CompareIdentity (targetEntry)) { - entry.KeyRefFound = true; - break; - } - } + if (xsdKeyref != null) + EndKeyrefValidation (seq, xsdKeyref.Target); + } + + private void EndKeyrefValidation (XsdKeyTable seq, XmlSchemaIdentityConstraint targetIdent) + { + for (int i = this.keyTables.Count - 1; i >= 0; i--) { + XsdKeyTable target = this.keyTables [i] as XsdKeyTable; + if (target.SourceSchemaIdentity != targetIdent) + continue; + 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."); - foreach (XsdKeyEntry entry in seq.FinishedEntries) { - if (!entry.KeyRefFound) - errors.Add (" line " + entry.SelectorLineNumber + ", position " + entry.SelectorLinePosition); + } + if (seq.ReferencedKey == null) + HandleError ("Target key was not found."); + ArrayList errors = null; + for (int i = 0; i < seq.FinishedEntries.Count; i++) { + XsdKeyEntry entry = (XsdKeyEntry) seq.FinishedEntries [i]; + if (!entry.KeyRefFound) { + if (errors == null) + errors = new ArrayList (); + 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 [])); } + if (errors != null) + HandleError ("Invalid identity constraints were found. Referenced key was not found: " + + String.Join (" / ", errors.ToArray (typeof (string)) as string [])); } +#endregion // Overrided Methods @@ -1329,7 +1245,7 @@ namespace Mono.Xml.Schema if (reader.AttributeCount > i) reader.GetAttribute (i); - int defIdx = i - nonDefaultAttributeCount; + int defIdx = i - reader.AttributeCount; if (i < AttributeCount) return defaultAttributes [defIdx].DefaultValue; @@ -1348,19 +1264,19 @@ namespace Mono.Xml.Schema if (value != null) return value; - XmlQualifiedName qname = SplitQName (name); + QName qname = SplitQName (name); return GetDefaultAttribute (qname.Name, qname.Namespace); } - private XmlQualifiedName SplitQName (string name) + private QName SplitQName (string name) { if (!XmlChar.IsName (name)) throw new ArgumentException ("Invalid name was specified.", "name"); Exception ex = null; - XmlQualifiedName qname = XmlSchemaUtil.ToQName (reader, name, out ex); + QName qname = XmlSchemaUtil.ToQName (reader, name, out ex); if (ex != null) - return XmlQualifiedName.Empty; + return QName.Empty; else return qname; } @@ -1394,7 +1310,7 @@ namespace Mono.Xml.Schema private int FindDefaultAttribute (string localName, string ns) { for (int i = 0; i < this.defaultAttributes.Length; i++) { - XmlSchemaAttribute attr = defaultAttributes [i]; + XsAttribute attr = defaultAttributes [i]; if (attr.QualifiedName.Name == localName && (ns == null || attr.QualifiedName.Namespace == ns)) return i; @@ -1402,7 +1318,7 @@ namespace Mono.Xml.Schema return -1; } - bool IXmlLineInfo.HasLineInfo () + public bool HasLineInfo () { return readerLineInfo != null && readerLineInfo.HasLineInfo (); } @@ -1421,15 +1337,15 @@ namespace Mono.Xml.Schema return; } - currentQName = null; - if (i < this.nonDefaultAttributeCount) { + currentAttrType = null; + if (i < reader.AttributeCount) { reader.MoveToAttribute (i); this.currentDefaultAttribute = -1; this.defaultAttributeConsumed = false; } if (i < AttributeCount) { - this.currentDefaultAttribute = i - nonDefaultAttributeCount; + this.currentDefaultAttribute = i - reader.AttributeCount; this.defaultAttributeConsumed = false; } else @@ -1444,7 +1360,7 @@ namespace Mono.Xml.Schema return reader.MoveToAttribute (name); } - currentQName = null; + currentAttrType = null; bool b = reader.MoveToAttribute (name); if (b) { this.currentDefaultAttribute = -1; @@ -1463,7 +1379,7 @@ namespace Mono.Xml.Schema return reader.MoveToAttribute (localName, ns); } - currentQName = null; + currentAttrType = null; bool b = reader.MoveToAttribute (localName, ns); if (b) { this.currentDefaultAttribute = -1; @@ -1488,7 +1404,7 @@ namespace Mono.Xml.Schema { currentDefaultAttribute = -1; defaultAttributeConsumed = false; - currentQName = null; + currentAttrType = null; return reader.MoveToElement (); } @@ -1500,8 +1416,8 @@ namespace Mono.Xml.Schema return reader.MoveToFirstAttribute (); } - currentQName = null; - if (this.nonDefaultAttributeCount > 0) { + currentAttrType = null; + if (reader.AttributeCount > 0) { bool b = reader.MoveToFirstAttribute (); if (b) { currentDefaultAttribute = -1; @@ -1527,7 +1443,7 @@ namespace Mono.Xml.Schema return reader.MoveToNextAttribute (); } - currentQName = null; + currentAttrType = null; if (currentDefaultAttribute >= 0) { if (defaultAttributes.Length == currentDefaultAttribute + 1) return false; @@ -1552,139 +1468,175 @@ namespace Mono.Xml.Schema return false; } + private XmlSchema ReadExternalSchema (string uri) + { + Uri absUri = resolver.ResolveUri ((BaseURI != "" ? new Uri (BaseURI) : null), uri); + string absUriString = absUri != null ? absUri.ToString () : String.Empty; + XmlTextReader xtr = null; + try { + xtr = new XmlTextReader (absUriString, + (Stream) resolver.GetEntity ( + absUri, null, typeof (Stream)), + NameTable); + return XmlSchema.Read ( + xtr, ValidationEventHandler); + } finally { + if (xtr != null) + xtr.Close (); + } + } + private void ExamineAdditionalSchema () { + if (resolver == null || ValidationType == ValidationType.None) + return; XmlSchema schema = null; string schemaLocation = reader.GetAttribute ("schemaLocation", XmlSchema.InstanceNamespace); + bool schemaAdded = false; if (schemaLocation != null) { string [] tmp = null; try { - schemaLocation = XmlSchemaDatatype.FromName ("token").Normalize (schemaLocation); + schemaLocation = XsDatatype.FromName ("token", XmlSchema.Namespace).Normalize (schemaLocation); tmp = schemaLocation.Split (XmlChar.WhitespaceChars); } catch (Exception ex) { - HandleError ("Invalid schemaLocation attribute format.", ex, true); + if (schemas.Count == 0) + 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) { - Uri absUri = null; + if (schemas.Count == 0) + HandleError ("Invalid schemaLocation attribute format."); + int i=0; + do { try { - absUri = new Uri ((this.BaseURI != "" ? new Uri (BaseURI) : null), tmp [i + 1]); - XmlTextReader xtr = new XmlTextReader (absUri.ToString ()); - schema = XmlSchema.Read (xtr, null); - } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-( - HandleError ("Could not resolve schema location URI: " + absUri, null, true); + for (; i < tmp.Length; i += 2) { + schema = ReadExternalSchema (tmp [i + 1]); + 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); + } + schema = null; + } + } + } catch (Exception) { + if (!schemas.Contains (tmp [i])) + HandleError (String.Format ("Could not resolve schema location URI: {0}", + i + 1 < tmp.Length ? tmp [i + 1] : String.Empty), null, true); + i += 2; continue; } - if (schema.TargetNamespace == null) - schema.TargetNamespace = tmp [i]; - else if (schema.TargetNamespace != tmp [i]) - HandleError ("Specified schema has different target namespace."); - } + } while (i < tmp.Length); } - if (schema != null) { - try { - schemas.Add (schema, resolver); - } catch (XmlSchemaException ex) { - HandleError (ex); - } - } - schema = null; string noNsSchemaLocation = reader.GetAttribute ("noNamespaceSchemaLocation", XmlSchema.InstanceNamespace); if (noNsSchemaLocation != null) { - Uri absUri = null; try { - absUri = new Uri ((this.BaseURI != "" ? new Uri (BaseURI) : null), noNsSchemaLocation); - XmlTextReader xtr = new XmlTextReader (absUri.ToString ()); - schema = XmlSchema.Read (xtr, null); + schema = ReadExternalSchema (noNsSchemaLocation); } catch (Exception) { // FIXME: (wishlist) It is bad manner ;-( - HandleError ("Could not resolve schema location URI: " + absUri, null, true); + if (schemas.Count != 0) + HandleError ("Could not resolve schema location URI: " + noNsSchemaLocation, null, true); } if (schema != null && schema.TargetNamespace != null) HandleError ("Specified schema has different target namespace."); } if (schema != null) { - try { - schema.Compile (ValidationEventHandler, resolver); + if (!schemas.Contains (schema.TargetNamespace)) { + schemaAdded = true; schemas.Add (schema); - } catch (XmlSchemaException ex) { - HandleError (ex); } } + // FIXME: should call Reprocess()? + if (schemaAdded) + schemas.Compile (); } public override bool Read () { - nonDefaultAttributeCount = 0; + validationStarted = true; currentDefaultAttribute = -1; defaultAttributeConsumed = false; - currentQName = null; - thisElementId = null; - defaultAttributes = new XmlSchemaAttribute [0]; - if (popContext) { - elementQNameStack.RemoveAt (elementQNameStack.Count - 1); - popContext = false; - } + currentAttrType = null; + defaultAttributes = emptyAttributeArray; bool result = reader.Read (); + + // FIXME: schemaLocation could be specified + // at any Depth. + if (reader.Depth == 0 && + reader.NodeType == XmlNodeType.Element) { + // If the reader is DTDValidatingReader (it + // is the default behavior of + // XmlValidatingReader) and DTD didn't appear, + // we could just use its source XmlReader. + DTDValidatingReader dtdr = reader as DTDValidatingReader; + if (dtdr != null && dtdr.DTD == null) + reader = dtdr.Source; + + ExamineAdditionalSchema (); + } + if (schemas.Count == 0) + return result; + if (!schemas.IsCompiled) + schemas.Compile (); + +#region ID Constraints + if (this.checkIdentity) + idManager.OnStartElement (); + // 3.3.4 ElementLocallyValidElement 7 = Root Valid. - if (!result && missingIDReferences.Count > 0) - HandleError ("There are missing ID references: " + - String.Join (" ", - this.missingIDReferences.ToArray (typeof (string)) as string [])); + if (!result && this.checkIdentity && + idManager.HasMissingIDReferences ()) + HandleError ("There are missing ID references: " + idManager.GetMissingIDString ()); +#endregion switch (reader.NodeType) { case XmlNodeType.Element: - nonDefaultAttributeCount = reader.AttributeCount; - - if (reader.Depth == 0) - ExamineAdditionalSchema (); - - this.elementQNameStack.Add (new XmlQualifiedName (reader.LocalName, reader.NamespaceURI)); +#region Key Constraints + if (checkKeyConstraints) + this.elementQNameStack.Add (new QName (reader.LocalName, reader.NamespaceURI)); +#endregion // If there is no schema information, then no validation is performed. - if (schemas.Count == 0) - break; - if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth) { - if (shouldValidateCharacters) { - ValidateEndCharacters (); - shouldValidateCharacters = false; - } + ValidateEndSimpleContent (); AssessStartElementSchemaValidity (); - storedCharacters.Length = 0; - } else { - context.Clear (); } if (reader.IsEmptyElement) goto case XmlNodeType.EndElement; - else + else if (xsiNilDepth < reader.Depth) shouldValidateCharacters = true; break; case XmlNodeType.EndElement: - if (reader.Depth == skipValidationDepth) { + if (reader.Depth == skipValidationDepth) skipValidationDepth = -1; - context.Clear (); - } - else + else if (skipValidationDepth < 0 || reader.Depth <= skipValidationDepth) AssessEndElementSchemaValidity (); - storedCharacters.Length = 0; - childParticleState = null; - popContext = true; + if (checkKeyConstraints) + elementQNameStack.RemoveAt (elementQNameStack.Count - 1); break; case XmlNodeType.CDATA: case XmlNodeType.SignificantWhitespace: + case XmlNodeType.Whitespace: case XmlNodeType.Text: - XmlSchemaComplexType ct = context.ActualType as XmlSchemaComplexType; - if (ct != null && storedCharacters.Length > 0) { + if (skipValidationDepth >= 0 && reader.Depth > skipValidationDepth) + break; + + ComplexType ct = Context.ActualType as ComplexType; + if (ct != null) { switch (ct.ContentType) { case XmlSchemaContentType.ElementOnly: + if (reader.NodeType != XmlNodeType.Whitespace) + HandleError (String.Format ("Not allowed character content is found (current content model '{0}' is element-only).", ct.QualifiedName)); + break; case XmlSchemaContentType.Empty: - HandleError ("Not allowed character content was found."); + HandleError (String.Format ("Not allowed character content is found (current element content model '{0}' is empty).", ct.QualifiedName)); break; } } @@ -1737,84 +1689,152 @@ namespace Mono.Xml.Schema { reader.ResolveEntity (); } + } - internal class XsdValidationContext + internal class XsdValidationContext + { + public XsdValidationContext () { - Hashtable contextStack; + } - public XsdValidationContext () - { - contextStack = new Hashtable (); - } + object xsi_type; + public object XsiType { get { return xsi_type; } set { xsi_type = value; } } // xsi:type + internal XsdValidationState State; + Stack element_stack = new Stack (); - // Some of them might be missing (See the spec section 5.3, and also 3.3.4). - public XmlSchemaElement Element; - public XsdValidationState ParticleState; - public XmlSchemaAttribute [] DefaultAttributes; + // Some of them might be missing (See the spec section 5.3, and also 3.3.4). + public XsElement Element { + get { return element_stack.Count > 0 ? element_stack.Peek () as XsElement : null; } + } - // Some of them might be missing (See the spec section 5.3). - public object SchemaType; + public void PushCurrentElement (XsElement element) + { + element_stack.Push (element); + } - public object LocalTypeDefinition; + public void PopCurrentElement () + { + element_stack.Pop (); + } - public object ActualType { - get { - if (LocalTypeDefinition != null) - return LocalTypeDefinition; - else - return SchemaType; - } + // Note that it represents current element's type. + public object ActualType { + get { + // FIXME: actually this should also be stacked + if (element_stack.Count == 0) + return null; + if (XsiType != null) + return XsiType; + else + return Element != null ? Element.ElementType : null; } + } - public void Clear () - { - Element = null; - SchemaType = null; - ParticleState = null; - LocalTypeDefinition = null; +#if NET_2_0 + public XmlSchemaType ActualSchemaType { + get { + object at = ActualType; + if (at == null) + return null; + XmlSchemaType st = at as XmlSchemaType; + if (st == null) + st = XmlSchemaType.GetBuiltInSimpleType ( + ((XmlSchemaDatatype) at).TypeCode); + return st; } + } +#endif - public void PushScope (int depth) - { - contextStack [depth] = this.MemberwiseClone (); - } + public bool IsInvalid { + get { return State == XsdValidationState.Invalid; } + } - public void PopScope (int depth) - { - Load (depth); - contextStack.Remove (depth + 1); - } + public object Clone () + { + return MemberwiseClone (); + } - public void Load (int depth) - { - Clear (); - XsdValidationContext restored = (XsdValidationContext) contextStack [depth]; - if (restored != null) { - this.Element = restored.Element; - this.ParticleState = restored.ParticleState; - this.SchemaType = restored.SchemaType; - this.LocalTypeDefinition = restored.LocalTypeDefinition; - } + public void EvaluateStartElement ( + string localName, string ns) + { + State = State.EvaluateStartElement (localName, ns); + } + + public bool EvaluateEndElement () + { + return State.EvaluateEndElement (); + } + } + + internal class XsdIDManager + { + public XsdIDManager () + { + } + + Hashtable idList = new Hashtable (); + ArrayList missingIDReferences; + string thisElementId; + + private ArrayList MissingIDReferences { + get { + if (missingIDReferences == null) + missingIDReferences = new ArrayList (); + return missingIDReferences; } } - /* - internal class XsdValidityState + public void OnStartElement () { - ArrayList currentParticles = new ArrayList (); - ArrayList occured = new ArrayList (); - Hashtable xsAllConsumed = new Hashtable (); - XmlSchemaParticle parciele; - int particleDepth; + thisElementId = null; + } - public XsdValidityState (XmlSchemaParticle particle) - { - this.parciele = particle; - currentParticles.Add (particle); + // 3.4.4-5 wild IDs + public string AssessEachAttributeIdentityConstraint ( + XsDatatype dt, object parsedValue, string elementName) + { + // Validate identity constraints. + string str = parsedValue as string; + switch (dt.TokenizedType) { + case XmlTokenizedType.ID: + if (thisElementId != null) + return "ID type attribute was already assigned in the containing element."; + else + thisElementId = str; + if (idList.ContainsKey (str)) + return "Duplicate ID value was found."; + else + idList.Add (str, elementName); + if (MissingIDReferences.Contains (str)) + MissingIDReferences.Remove (str); + break; + case XmlTokenizedType.IDREF: + if (!idList.Contains (str) && !MissingIDReferences.Contains (str)) + MissingIDReferences.Add (str); + break; + case XmlTokenizedType.IDREFS: + string [] idrefs = (string []) parsedValue; + for (int i = 0; i < idrefs.Length; i++) { + string id = idrefs [i]; + if (!idList.Contains (id) && !MissingIDReferences.Contains (str)) + MissingIDReferences.Add (id); + } + break; } + return null; + } + public bool HasMissingIDReferences () + { + return missingIDReferences != null + && missingIDReferences.Count > 0; } - */ - } + public string GetMissingIDString () + { + return String.Join (" ", + MissingIDReferences.ToArray (typeof (string)) + as string []); + } + } }