2008-12-08 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.Schema / XmlSchemaValidator.cs
index 1790c478aa857ac4c3ac2c066264dedd251d12d9..a1d4e9d152f235cb71697aac3d65b2a4f29da7fe 100644 (file)
@@ -153,8 +153,11 @@ namespace System.Xml.Schema
                int xsiNilDepth = -1;
                int skipValidationDepth = -1;
 
-               SOMObject currentType;
-
+               // LAMESPEC: XmlValueGetter is bogus by design because there
+               // is no way to get associated schema type for current value.
+               // Here XmlSchemaValidatingReader needs "current type"
+               // information to validate attribute values.
+               internal XmlSchemaDatatype CurrentAttributeType;
 
                #endregion
 
@@ -238,7 +241,7 @@ namespace System.Xml.Schema
                                al.Add (p);
                }
 
-               [MonoTODO ("Need some tests.")]
+               [MonoTODO] // Need some tests.
                // Its behavior is not obvious. For example, it does not
                // contain groups (xs:sequence/xs:choice/xs:all). Since it
                // might contain xs:any, it could not be of type element[].
@@ -255,40 +258,47 @@ namespace System.Xml.Schema
                                typeof (XmlSchemaParticle));
                }
 
-               public void GetUnspecifiedDefaultAttributes (ArrayList list)
+               public void GetUnspecifiedDefaultAttributes (ArrayList defaultAttributeList)
                {
+                       if (defaultAttributeList == null)
+                               throw new ArgumentNullException ("defaultAttributeList");
+
                        if (transition != Transition.StartTag)
                                throw new InvalidOperationException ("Method 'GetUnsoecifiedDefaultAttributes' works only when the validator state is inside a start tag.");
                        foreach (XmlSchemaAttribute attr
                                in GetExpectedAttributes ())
                                if (attr.ValidatedDefaultValue != null || attr.ValidatedFixedValue != null)
-                                       list.Add (attr);
+                                       defaultAttributeList.Add (attr);
 
-                       list.AddRange (defaultAttributes);
+                       defaultAttributeList.AddRange (defaultAttributes);
                }
 
                // State Controller
 
                public void AddSchema (XmlSchema schema)
                {
+                       if (schema == null)
+                               throw new ArgumentNullException ("schema");
                        schemas.Add (schema);
                        schemas.Compile ();
                }
 
                public void Initialize ()
                {
-                       Initialize (null);
-               }
-
-               public void Initialize (SOMObject startType)
-               {
-                       this.startType = startType;
                        transition = Transition.Content;
                        state = new XsdParticleStateManager ();
                        if (!schemas.IsCompiled)
                                schemas.Compile ();
                }
 
+               public void Initialize (SOMObject partialValidationType)
+               {
+                       if (partialValidationType == null)
+                               throw new ArgumentNullException ("partialValidationType");
+                       this.startType = partialValidationType;
+                       Initialize ();
+               }
+
                // It must be called at the end of the validation (to check
                // identity constraints etc.).
                public void EndValidation ()
@@ -309,7 +319,7 @@ namespace System.Xml.Schema
                }
 
                // I guess it is for validation error recovery
-               [MonoTODO ("Find out how XmlSchemaInfo is used.")]
+               [MonoTODO] // FIXME: Find out how XmlSchemaInfo is used.
                public void SkipToEndElement (XmlSchemaInfo info)
                {
                        CheckState (Transition.Content);
@@ -324,16 +334,16 @@ namespace System.Xml.Schema
                        string attributeValue,
                        XmlSchemaInfo info)
                {
-                       return ValidateAttribute (localName, ns,
-                               delegate () { return info.SchemaType.Datatype.ParseValue (attributeValue, nameTable, nsResolver); },
-                               info);
+                       if (attributeValue == null)
+                               throw new ArgumentNullException ("attributeValue");
+                       return ValidateAttribute (localName, ns, delegate () { return attributeValue; }, info);
                }
 
                // I guess this weird XmlValueGetter is for such case that
                // value might not be required (and thus it improves 
                // performance in some cases. Doh).
 
-               // The return value is typed primitive, is possible.
+               // The return value is typed primitive, if possible.
                // AttDeriv
                public object ValidateAttribute (
                        string localName,
@@ -341,6 +351,13 @@ namespace System.Xml.Schema
                        XmlValueGetter attributeValue,
                        XmlSchemaInfo info)
                {
+                       if (localName == null)
+                               throw new ArgumentNullException ("localName");
+                       if (ns == null)
+                               throw new ArgumentNullException ("ns");
+                       if (attributeValue == null)
+                               throw new ArgumentNullException ("attributeValue");
+
                        CheckState (Transition.StartTag);
 
                        QName qname = new QName (localName, ns);
@@ -354,11 +371,14 @@ namespace System.Xml.Schema
                        if (schemas.Count == 0)
                                return null;
 
-                       // 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.");
+                       if (Context.Element != null && Context.XsiType == null) {
+
+                               // 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;
                }
 
@@ -380,6 +400,11 @@ namespace System.Xml.Schema
                        string schemaLocation,
                        string noNsSchemaLocation)
                {
+                       if (localName == null)
+                               throw new ArgumentNullException ("localName");
+                       if (ns == null)
+                               throw new ArgumentNullException ("ns");
+
                        CheckState (Transition.Content);
                        transition = Transition.StartTag;
 
@@ -435,7 +460,7 @@ namespace System.Xml.Schema
                // represented by current simple content type. (try passing
                // some kind of object to this method to check the behavior.)
                // EndTagDeriv
-               [MonoTODO ("Handle 'var' parameter.")]
+               [MonoTODO] // FIXME: Handle 'var' parameter.
                public object ValidateEndElement (XmlSchemaInfo info,
                        object var)
                {
@@ -473,7 +498,9 @@ namespace System.Xml.Schema
                                if (schemas.Count == 0)
                                        return;
 
-                               AssessCloseStartElementSchemaValidity (info);
+                               if (skipValidationDepth < 0 || depth <= skipValidationDepth)
+                                       AssessCloseStartElementSchemaValidity (info);
+                               depth++;
                        } finally {
                                occuredAtts.Clear ();
                        }
@@ -484,6 +511,8 @@ namespace System.Xml.Schema
                // or content type validation errors.
                public void ValidateText (string value)
                {
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
                        ValidateText (delegate () { return value; });
                }
 
@@ -491,17 +520,24 @@ namespace System.Xml.Schema
                // ValidateAtomicValue().
                public void ValidateText (XmlValueGetter getter)
                {
+                       if (getter == null)
+                               throw new ArgumentNullException ("getter");
+
                        CheckState (Transition.Content);
                        if (schemas.Count == 0)
                                return;
 
                        ComplexType ct = Context.ActualType as ComplexType;
-                       if (ct != null && storedCharacters.Length > 0) {
+                       if (ct != null) {
                                switch (ct.ContentType) {
-                               case XmlSchemaContentType.ElementOnly:
                                case XmlSchemaContentType.Empty:
                                        HandleError ("Not allowed character content was found.");
                                        break;
+                               case XmlSchemaContentType.ElementOnly:
+                                       string s = storedCharacters.ToString ();
+                                       if (s.Length > 0 && !XmlChar.IsWhitespace (s))
+                                               HandleError ("Not allowed character content was found.");
+                                       break;
                                }
                        }
 
@@ -510,18 +546,15 @@ namespace System.Xml.Schema
 
                public void ValidateWhitespace (string value)
                {
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
                        ValidateWhitespace (delegate () { return value; });
                }
 
-               // TextDeriv...?
-               [MonoTODO]
+               // TextDeriv. It should do the same as ValidateText() in our actual implementation (whitespaces are conditioned).
                public void ValidateWhitespace (XmlValueGetter getter)
                {
-                       CheckState (Transition.Content);
-                       if (schemas.Count == 0)
-                               return;
-
-//                     throw new NotImplementedException ();
+                       ValidateText (getter);
                }
 
                #endregion
@@ -581,7 +614,7 @@ namespace System.Xml.Schema
                                        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.",
+                                               String.Format ("Unexpected attempt to validate state transition from {0} to {1}.",
                                                        transition,
                                                        expected));
                        }
@@ -611,7 +644,7 @@ namespace System.Xml.Schema
                        if (Context.IsInvalid)
                                HandleError ("Invalid start element: " + ns + ":" + localName);
 
-                       Context.SetElement (state.CurrentElement);
+                       Context.PushCurrentElement (state.CurrentElement);
                }
 
                private void AssessOpenStartElementSchemaValidity (
@@ -628,7 +661,7 @@ namespace System.Xml.Schema
                        // [Schema Validity Assessment (Element) 1.1]
                        if (Context.Element == null) {
                                state.CurrentElement = FindElement (localName, ns);
-                               Context.SetElement (state.CurrentElement);
+                               Context.PushCurrentElement (state.CurrentElement);
                        }
 
 #region Key Constraints
@@ -685,8 +718,6 @@ namespace System.Xml.Schema
                                        next = state.Create (XmlSchemaParticle.Empty);
                        }
                        Context.State = next;
-
-                       depth++;
                }
 
                // It must be invoked after xsi:nil turned out not to be in
@@ -787,15 +818,21 @@ namespace System.Xml.Schema
                        if (dt != SimpleType.AnySimpleType || attr.ValidatedFixedValue != null) {
                                object parsedValue = null;
                                try {
+                                       CurrentAttributeType = dt;
                                        parsedValue = getter ();
                                } catch (Exception ex) { // It is inevitable and bad manner.
-                                       HandleError ("Attribute value is invalid against its data type " + dt.TokenizedType, ex);
+                                       HandleError (String.Format ("Attribute value is invalid against its data type {0}", dt != null ? dt.TokenizedType : default (XmlTokenizedType)), ex);
                                }
-                               XmlSchemaType type = info != null ? info.SchemaType : null;
+
+                               // check part of 3.14.4 StringValid
+                               SimpleType st = attr.AttributeType as SimpleType;
+                               if (st != null)
+                                       ValidateRestrictedSimpleTypeValue (st, ref dt, new XmlAtomicValue (parsedValue, attr.AttributeSchemaType).Value);
+
                                if (attr.ValidatedFixedValue != null && 
-                                       !XmlSchemaUtil.IsSchemaDatatypeEquals (
-                                       attr.AttributeSchemaType.Datatype as XsdAnySimpleType, attr.ValidatedFixedTypedValue, type != null ? type.Datatype as XsdAnySimpleType : null, parsedValue)) {
-                                       HandleError ("The value of the attribute " + attr.QualifiedName + " does not match with its fixed value.");
+                                       !XmlSchemaUtil.AreSchemaDatatypeEqual (
+                                       attr.AttributeSchemaType.Datatype as XsdAnySimpleType, attr.ValidatedFixedTypedValue, dt as XsdAnySimpleType, parsedValue)) {
+                                       HandleError (String.Format ("The value of the attribute {0} does not match with its fixed value '{1}' in the space of type {2}", attr.QualifiedName, attr.ValidatedFixedValue, dt));
                                        parsedValue = attr.ValidatedFixedTypedValue;
                                }
 #region ID Constraints
@@ -832,10 +869,10 @@ namespace System.Xml.Schema
                private object AssessEndElementSchemaValidity (
                        XmlSchemaInfo info)
                {
-                       ValidateEndElementParticle ();  // validate against childrens' state.
-
                        object ret = ValidateEndSimpleContent (info);
 
+                       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)
                        // => ValidateEndSimpleContent ().
@@ -858,6 +895,7 @@ namespace System.Xml.Schema
                                        HandleError ("Invalid end element. There are still required content items.");
                                }
                        }
+                       Context.PopCurrentElement ();
                        state.PopContext ();
                }
 
@@ -867,8 +905,10 @@ namespace System.Xml.Schema
                        if (xsiNilDepth >= 0 && xsiNilDepth < depth)
                                HandleError ("Element item appeared, while current element context is nil.");
 
-                       if (shouldValidateCharacters)
+                       if (shouldValidateCharacters) {
+                               CurrentAttributeType = null;
                                storedCharacters.Append (getter ());
+                       }
                }
 
 
@@ -909,9 +949,12 @@ namespace System.Xml.Schema
                                        dt = ct.Datatype;
                                        switch (ct.ContentType) {
                                        case XmlSchemaContentType.ElementOnly:
+                                               if (value.Length > 0 && !XmlChar.IsWhitespace (value))
+                                                       HandleError ("Character content not allowed in an elementOnly model.");
+                                               break;
                                        case XmlSchemaContentType.Empty:
                                                if (value.Length > 0)
-                                                       HandleError ("Character content not allowed.");
+                                                       HandleError ("Character content not allowed in an empty model.");
                                                break;
                                        }
                                }
@@ -1033,7 +1076,7 @@ namespace System.Xml.Schema
                                                if (baseType != null) {
                                                         ret = AssessStringValid (baseType, dt, value);
                                                }
-                                               if (!str.ValidateValueWithFacets (value, nameTable)) {
+                                               if (!str.ValidateValueWithFacets (value, nameTable, nsResolver)) {
                                                        HandleError ("Specified value was invalid against the facets.");
                                                        break;
                                                }
@@ -1052,6 +1095,91 @@ namespace System.Xml.Schema
                        return ret;
                }
 
+               private void ValidateRestrictedSimpleTypeValue (SimpleType st, ref XsDatatype dt, string normalized)
+               {
+                       {
+                               string [] values;
+                               XsDatatype itemDatatype;
+                               SimpleType itemSimpleType;
+                               switch (st.DerivedBy) {
+                               case XmlSchemaDerivationMethod.List:
+                                       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, 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);
+                                       }
+                                       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 {
+                                                                       itemDatatype.ParseValue (each, nameTable, nsResolver);
+                                                               } catch (Exception) { // FIXME: (wishlist) better exception handling ;-(
+                                                                       continue;
+                                                               }
+                                                       }
+                                                       else {
+                                                               try {
+                                                                       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) {
+                                                        AssessStringValid(baseType, dt, normalized);
+                                               }
+                                               if (!str.ValidateValueWithFacets (normalized, nameTable, nsResolver)) {
+                                                       HandleError ("Specified value was invalid against the facets.");
+                                                       break;
+                                               }
+                                       }
+                                       dt = st.Datatype;
+                                       break;
+                               }
+                       }
+               }
+
                #endregion
 
                #region Key Constraints Validation
@@ -1099,6 +1227,7 @@ namespace System.Xml.Schema
                                XsdKeyTable seq  = (XsdKeyTable) keyTables [i];
                                // If possible, create new field entry candidates.
                                for (int j = 0; j < seq.Entries.Count; j++) {
+                                       CurrentAttributeType = null;
                                        try {
                                                seq.Entries [j].ProcessMatch (
                                                        isAttr,
@@ -1124,10 +1253,6 @@ namespace System.Xml.Schema
                        }
                }
 
-               private void ProcessKeyEntryOne (XsdKeyEntry entry, bool isAttr, bool isNil, object schemaType, string attrName, string attrNs, XmlValueGetter getter)
-               {
-               }
-
                private void ValidateEndElementKeyConstraints ()
                {
                        // Reset Identity constraints.