public override ValidationErrorInfo Validate(ValidationContext context)
        {
            string minAttributeValue = context.Element.GetAttributeValueEx(_minAttributeLocalName, _minAttributeNamesapce);

            double minValue;

            //If value cannot be converted into double, that means attribute type is not correct.
            //That's job of schema validation, semantic validation will do nothing to avoid throw duplicated error.
            if (!double.TryParse(minAttributeValue, out minValue))
            {
                return null;
            }

            string maxAttributeValue = context.Element.GetAttributeValueEx(_maxAttributeLocalName, _maxAttributeNamesapce);

            double maxValue;

            //If value cannot be converted into double, that means attribute type is not correct.
            //That's job of schema validation, semantic validation will do nothing to avoid throw duplicated error.
            if (!double.TryParse(maxAttributeValue, out maxValue))
            {
                return null;
            }

            if (minValue <= maxValue)
            {
                return null;
            }

            string errorId = ""; //todo: add error id
            string errorMessage = ""; //todo: add error message

            return new ValidationErrorInfo() { Id = errorId, ErrorType = ValidationErrorType.Semantic, Node = context.Element, Description = errorMessage };
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            if (_values == null)
            {
                return null;
            }
            
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_attribute];

            //if the attribute is omited, semantic validation will do nothing 
            if (attributeValue == null || string.IsNullOrEmpty(attributeValue.InnerText))
            {
                return null;
            }

            if (_values.Where(v => string.Compare(v, attributeValue.InnerText, !_caseSensitive, System.Globalization.CultureInfo.InvariantCulture) == 0).Count() == 0)
            {
                _values.Add(attributeValue.InnerText);
                return null;
            }

            return new ValidationErrorInfo()
            {
                Id = "Sem_UniqueAttributeValue",
                ErrorType = ValidationErrorType.Semantic,
                Node = context.Element,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_UniqueAttributeValue,
                                            GetAttributeQualifiedName(context.Element, _attribute), attributeValue.InnerText)
            };
        }
Ejemplo n.º 3
0
        protected static OpenXmlPart GetReferencedPart(ValidationContext context, string path)
        {
            if (path == ".")
            {
                return context.Part;
            }

            string[] parts = path.Split('/');

            if (string.IsNullOrEmpty(parts[0]))
            {
                return GetPartThroughPartPath(context.Package.Parts, parts.Skip(1).ToArray()); //absolute path
            }
            else if (parts[0] == "..")
            {
                IEnumerable<OpenXmlPart> iterator = new OpenXmlPackagePartIterator(context.Package);

                IEnumerable<OpenXmlPart> refParts = iterator.Where(p => p.Parts.Select(r => r.OpenXmlPart.PackagePart.Uri)
                                                            .Contains(context.Part.PackagePart.Uri));

                Debug.Assert(refParts.Count() == 1);

                return refParts.First();
            }
            else
            {
                return GetPartThroughPartPath(context.Part.Parts, parts); //relative path
            }
        }
 /// <summary>
 /// Try match the particle.
 /// </summary>
 /// <param name="particleMatchInfo"></param>
 /// <param name="validationContext">The context information for validation.</param>
 public override void TryMatch(ParticleMatchInfo particleMatchInfo, ValidationContext validationContext)
 {
     // maxOccurs of xsd:any can only be 1
     Debug.Assert(this.ParticleConstraint.MaxOccurs == 1);
      
     this.TryMatchOnce(particleMatchInfo, validationContext);
 }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_attribute];

            //if the attribute is omited, semantic validation will do nothing 
            if (attributeValue == null || string.IsNullOrEmpty(attributeValue.InnerText))
            {
                return null;
            }

            int index;
            if (!int.TryParse(attributeValue, out index))
            {
                return null; //if attribute is not int, schema validation will cover this error.
            }

            if (index < GetRefElementCount(context) + _indexBase)
            {
                return null;
            }

            return new ValidationErrorInfo()
            {
                Id = "Sem_MissingIndexedElement",
                ErrorType = ValidationErrorType.Semantic,
                Node = context.Element,
                RelatedPart = this._relatedPart,
                RelatedNode = null,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_MissingIndexedElement, 
                                            _refElementName,context.Element.LocalName, 
                                            GetAttributeQualifiedName(context.Element, _attribute), 
                                            _relatedPart == null? _refPartType : _relatedPart.PackagePart.Uri.ToString(), index)
            };
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_refAttribute];

            if (attributeValue == null || string.IsNullOrEmpty(attributeValue.InnerText))
            {
                return null;
            }

            if (GetReferencedAttributes(context).Contains(attributeValue.InnerText))
            {
                return null;
            }

            return new ValidationErrorInfo()
            {
                Id = "Sem_MissingReferenceElement",
                ErrorType = ValidationErrorType.Semantic,
                Node = context.Element,
                RelatedPart = this._relatedPart,
                RelatedNode = null,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, 
                                            ValidationResources.Sem_MissingReferenceElement, _elementName, context.Element.LocalName, 
                                            GetAttributeQualifiedName(context.Element, _refAttribute), _relatedPart == null ? _partPath : _relatedPart.PackagePart.Uri.ToString(), attributeValue.InnerText)
            };
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_rIdAttribute];

            //if the attribute is omited, semantic validation will do nothing 
            if (attributeValue == null || string.IsNullOrEmpty(attributeValue.InnerText))
            {
                return null;
            }

            if (context.Part.PackagePart.RelationshipExists(attributeValue.InnerText))
            {
                return null;
            }
            else
            {
                string errorDescription = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_InvalidRelationshipId,
                                                        attributeValue, GetAttributeQualifiedName(context.Element, _rIdAttribute));

                return new ValidationErrorInfo()
                {
                    Id = "Sem_InvalidRelationshipId",
                    ErrorType = ValidationErrorType.Semantic,
                    Node = context.Element,
                    Description = errorDescription
                };
            }
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_attribute];

            //if the attribute is omited, semantic validation will do nothing 
            if (attributeValue == null || string.IsNullOrEmpty(attributeValue.InnerText))
            {
                return null;
            }

            Regex regex = new Regex(this._pattern);
            if (regex.IsMatch(attributeValue.InnerText))
            {
                return null;
            }

            string subMsg = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sch_PatternConstraintFailed, _pattern);
            return new ValidationErrorInfo()
            {
                Id = "Sem_AttributeValueDataTypeDetailed",
                ErrorType = ValidationErrorType.Schema,
                Node = context.Element,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_AttributeValueDataTypeDetailed,
                                            GetAttributeQualifiedName(context.Element, _attribute), attributeValue.InnerText, subMsg)
            };
        }
Ejemplo n.º 9
0
        public void Validate(ValidationContext validationContext)
        {
            Debug.Assert(validationContext != null);
            Debug.Assert(validationContext.Element != null);

            this._stopValidating = false;

            ValidationTraverser.ValidatingTraverse(validationContext, this.ValidateElement, OnContextValidationFinished, this.StopSignal);
        }
Ejemplo n.º 10
0
 private void ValidateElement(ValidationContext context)
 {
     if (_curReg != null)
     {
         foreach (var error in _curReg.CheckConstraints(context))
         {
             context.EmitError(error);
         }
     }
 }
        /// <summary>
        /// Try match the particle once.
        /// </summary>
        /// <param name="particleMatchInfo"></param>
        /// <param name="validationContext">The context information for validation.</param>
        public override void TryMatchOnce(ParticleMatchInfo particleMatchInfo, ValidationContext validationContext)
        {
            Debug.Assert(!(particleMatchInfo.StartElement is OpenXmlMiscNode));
            
            var next = particleMatchInfo.StartElement;

            particleMatchInfo.LastMatchedElement = null;
            particleMatchInfo.Match = ParticleMatch.Nomatch;

            ParticleConstraint childConstraint;
            int constraintIndex = 0;
            int constraintTotal = this.ParticleConstraint.ChildrenParticles.Length;

            while (constraintIndex < constraintTotal && next != null)
            {
                childConstraint = this.ParticleConstraint.ChildrenParticles[constraintIndex];

                // Use Reset() instead of new() to avoid heavy memory alloction and GC.
                _childMatchInfo.Reset(next);

                childConstraint.ParticleValidator.TryMatch(_childMatchInfo, validationContext);

                // if the _childMatchInfo.StartElement is changed, it means this method of this object is called more than once on the stack.
                Debug.Assert(_childMatchInfo.StartElement == next);

                switch (_childMatchInfo.Match)
                {
                    case ParticleMatch.Nomatch:
                        // continue trying match next child constraint.
                        constraintIndex++;
                        break;

                    case ParticleMatch.Matched:
                        particleMatchInfo.Match = ParticleMatch.Matched;
                        particleMatchInfo.LastMatchedElement = _childMatchInfo.LastMatchedElement;
                        return;

                    case ParticleMatch.Partial:
                        // partial match, incomplete children.
                        particleMatchInfo.Match = ParticleMatch.Partial;
                        particleMatchInfo.LastMatchedElement = _childMatchInfo.LastMatchedElement;
                        if (validationContext.CollectExpectedChildren)
                        {
                            particleMatchInfo.SetExpectedChildren(_childMatchInfo.ExpectedChildren);
                        }
                        return;
                }
            }

            // no match
            Debug.Assert(particleMatchInfo.Match == ParticleMatch.Nomatch);
            return;
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_requiredAttribute];

            if (attributeValue != null)
            {
                return null;
            }

            OpenXmlSimpleType conditionAttributeValue = context.Element.Attributes[_conditionAttribute];

            if (conditionAttributeValue == null)
            {
                return null;
            }

            foreach (string value in _values)
            {
                if (AttributeValueEquals(conditionAttributeValue, value, false))
                {
                    string valueString = "'" + _values[0] + "'";

                    if (_values.Length > 1)
                    {
                        for (int i = 1; i < _values.Length - 1; i++)
                        {
                            valueString += ", '" + _values[i] + "'";
                        }

                        valueString += " or '" + _values[_values.Length - 1] + "'";
                    }

                    

                    return new ValidationErrorInfo()
                    {
                        Id = "Sem_AttributeRequiredConditionToValue",
                        ErrorType = ValidationErrorType.Semantic,
                        Node = context.Element,
                        Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_AttributeRequiredConditionToValue,
                                                    GetAttributeQualifiedName(context.Element, _requiredAttribute),                            
                                                    GetAttributeQualifiedName(context.Element, _conditionAttribute),
                                                    valueString)
                    };
                }
            }

            return null;
        }
Ejemplo n.º 13
0
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlElement parent = context.Element.Parent;

            if (parent == null)
            {
                return null;
            }

            if (parent.GetType() == this._parentType ^ !this._isValid) //TODO: (junzha) need to take ac-block into account.
            {
                return null;
            }

            return new ValidationErrorInfo() { Id = "", ErrorType = ValidationErrorType.Semantic, Node = context.Element, Description = "" };
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            if (context.Element.Attributes[_attribute] != null)
            {
                return null;
            }

            return new ValidationErrorInfo() 
            { 
                Id = "Sem_MissRequiredAttribute", 
                ErrorType = ValidationErrorType.Schema, 
                Node = context.Element,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sch_MissRequiredAttribute, 
                                            GetAttributeQualifiedName(context.Element, _attribute)) 
            };
        }
Ejemplo n.º 15
0
        // ***********************************************************
        //<xsd:group ref="..." /> is valid under <xsd:complexType>
        //<xsd:complexType name="CT_HdrFtr">
        //  <xsd:group ref="EG_BlockLevelElts" minOccurs="1" maxOccurs="unbounded" />
        //</xsd:complexType>
        // ***********************************************************

        ///// <summary>
        ///// Be called on root particle of complex type.
        ///// </summary>
        ///// <param name="validationContext"></param>
        ///// <returns></returns>
        //internal override SchemaValidationResult Validate(ValidationContext validationContext)
        //{
        //    throw new InvalidOperationException();
        //}


        /// <summary>
        /// Try match the particle once.
        /// </summary>
        /// <param name="particleMatchInfo"></param>
        /// <param name="validationContext">The context information for validation.</param>
        public override void TryMatchOnce(ParticleMatchInfo particleMatchInfo, ValidationContext validationContext)
        {
            Debug.Assert(!(particleMatchInfo.StartElement is OpenXmlMiscNode));
            
            // group only contains xsd:all, xsd:choice or xsd:sequence
            Debug.Assert(this.ParticleConstraint.ChildrenParticles.Length == 1);

            var childParticle = this.ParticleConstraint.ChildrenParticles[0];

            Debug.Assert(childParticle.ParticleType == ParticleType.All ||
                        childParticle.ParticleType == ParticleType.Choice ||
                        childParticle.ParticleType == ParticleType.Sequence);

            childParticle.ParticleValidator.TryMatch(particleMatchInfo, validationContext);

            return;
        }
 public override void ClearState(ValidationContext context)
 {
     if (context == null) //initialize before validating
     {
         _stateStack.Clear();
         _values = _partLevel ? new List<string>() : null;
     }
     else //unique scope element reached
     {
         if (_values != null)
         {
             _stateStack.Push(_values);
         }
         _reg.AddCallBackMethod(context.Element, this.AdjustState);
         _values = new List<string>();
     }
 }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_attribute];

            if (attributeValue == null)
            {
                return null;
            }

            double val;
            if (!GetAttrNumVal(attributeValue, out val))
            {
                return null;
            }

            OpenXmlSimpleType otherAttributeValue = context.Element.Attributes[_otherAttribute];

            if (otherAttributeValue == null)
            {
                return null;
            }

            double otherVal;
            if (!GetAttrNumVal(otherAttributeValue, out otherVal))
            {
                return null;
            }

            if (val < otherVal && !_canEqual || val <= otherVal && _canEqual)
            {
                return null;
            }

            string format = _canEqual ? ValidationResources.Sem_AttributeValueLessEqualToAnother : ValidationResources.Sem_AttributeValueLessEqualToAnotherEx;

            return new ValidationErrorInfo()
            {
                Id = "Sem_AttributeValueLessEqualToAnother",
                ErrorType = ValidationErrorType.Semantic,
                Node = context.Element,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, format,
                                            GetAttributeQualifiedName(context.Element, _attribute), attributeValue.InnerText,
                                            GetAttributeQualifiedName(context.Element, _otherAttribute), otherAttributeValue.InnerText)
            };
        }
Ejemplo n.º 18
0
        public void AttributeMinMaxConstraintTest()
        {
            Excel.Column column = new Excel.Column();
            ValidationContext context = new ValidationContext() { Element = column };

            AttributeMinMaxConstraint constraint = new AttributeMinMaxConstraint("", "min", "", "max") ;

            column.Max = 2;
            column.Min = 1;
            Assert.Null(constraint.Validate(context)); //max > min, should pass validation

            column.Max = 2;
            column.Min = 2;
            Assert.Null(constraint.Validate(context)); //max == min, should pass validation

            column.Max = 2;
            column.Min = 3;
            Assert.NotNull(constraint.Validate(context)); //max < min, validation should be failed.
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_attribute];

            if (attributeValue == null || string.IsNullOrEmpty(attributeValue.InnerText))
            {
                return null;
            }

            string actualType = _type;

            IEnumerable<ExternalRelationship> rels = context.Part.ExternalRelationships.Where(r => r.Id == attributeValue.InnerText);
            
            if (rels.Count() == 0)
            {
                IEnumerable<IdPartPair> pairs = context.Part.Parts.Where(p => p.RelationshipId == attributeValue.InnerText);
                if (pairs.Count() != 0)
                {
                    Debug.Assert(pairs.Count() == 1);
                    actualType = pairs.First().OpenXmlPart.RelationshipType;
                }
            }
            else
            {
                Debug.Assert(rels.Count() == 1);
                actualType = rels.First().RelationshipType;
            }

            if (actualType == _type)
            {
                return null;
            }

            return new ValidationErrorInfo()
            {
                Id = "Sem_IncorrectRelationshipType",
                ErrorType = ValidationErrorType.Semantic,
                Node = context.Element,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_IncorrectRelationshipType,
                                            actualType, GetAttributeQualifiedName(context.Element, _attribute), this._type)
            };
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Validate the DOM tree under the specified OpenXmlElement.
        /// </summary>
        /// <param name="schemaValidator">The schemaValidator.</param>
        /// <param name="openxmlElement">The root of the sub tree.</param>
        /// <returns>Returns the validation result in ValidationResult.</returns>
        /// <remarks>
        /// Only schema validating.
        /// </remarks>
        internal static ValidationResult Validate(this SchemaValidator schemaValidator, OpenXmlElement openxmlElement)
        {
            Debug.Assert(openxmlElement != null);

            var validationResult = new ValidationResult();

            Debug.Assert(!(openxmlElement is OpenXmlUnknownElement || openxmlElement is OpenXmlMiscNode));

            // Can not just validate AlternateContent / Choice / Fallback
            // Debug.Assert(!(openxmlElement is AlternateContent))
            Debug.Assert(!(openxmlElement is AlternateContentChoice || openxmlElement is AlternateContentFallback));

            var validationContext = new ValidationContext();
            validationContext.ValidationErrorEventHandler += validationResult.OnValidationError;

            validationContext.Element = openxmlElement;

            schemaValidator.Validate(validationContext);

            return validationResult;
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Validate the DOM tree under the specified OpenXmlElement in the context.
        /// </summary>
        /// <param name="validationContext"></param>
        internal void Validate(ValidationContext validationContext)
        {
            Debug.Assert(validationContext != null);
            Debug.Assert(validationContext.Element != null);

            this._stopValidating = false;

            var openxmlElement = validationContext.Element;

            Debug.Assert(!(openxmlElement is OpenXmlUnknownElement || openxmlElement is OpenXmlMiscNode));

            // Can not just validate AlternateContent / Choice / Fallback
            // Debug.Assert(!(openxmlElement is AlternateContent))
            Debug.Assert(!(openxmlElement is AlternateContentChoice || openxmlElement is AlternateContentFallback));

            ValidationTraverser.ValidatingTraverse(validationContext, this.ValidateElement, null, this.StopSignal);


            // validationContext.Element = openxmlElement;

            return;
        }
Ejemplo n.º 22
0
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            bool attribute1Exist = context.Element.GetAttributeValueEx(this._attribute1LocalName, this._attribute1Namespace) != null;
            bool attribute2Exist = context.Element.GetAttributeValueEx(this._attribute2LocalName, this._attribute2Namespace) != null;

            //if (attribute1Exist)
            //{
            //    this.MissedAttribute = this._attribute2LocalName;
            //    this.ExistAttribute = this._attribute1LocalName;
            //}
            //else
            //{
            //    this.MissedAttribute = this._attribute1LocalName;
            //    this.ExistAttribute = this._attribute2LocalName;
            //}

            if (!(attribute1Exist ^ attribute2Exist))
            {
                return null;
            }

            return new ValidationErrorInfo() { Id = "", ErrorType = ValidationErrorType.Semantic, Node = context.Element, Description = "" };
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attributeValue = context.Element.Attributes[_attribute];

            //if the attribute is omited, semantic validation will do nothing 
            if (attributeValue == null || string.IsNullOrEmpty(attributeValue.InnerText))
            {
                return null;
            }

            bool valueSetContains = false;

            foreach (string value in _valueSet)
            {
                if (AttributeValueEquals(attributeValue, value, false))
                {
                    valueSetContains = true;
                }
            }

            if (!this._isValidValueSet ^ valueSetContains)
            {
                return null;
            }

            string subMsg = ValidationResources.Sch_EnumerationConstraintFailed;
            string errorDescription = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_AttributeValueDataTypeDetailed,
                                                    GetAttributeQualifiedName(context.Element, _attribute), attributeValue, subMsg);

            return new ValidationErrorInfo()
            {
                Id = "Sem_AttributeValueDataTypeDetailed",
                ErrorType = ValidationErrorType.Schema,
                Node = context.Element,
                Description = errorDescription
            };
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            OpenXmlSimpleType attribute = context.Element.Attributes[_attribute];

            //if the attribute is omited, semantic validation will do nothing 
            if (attribute == null)
            {
                return null;
            }

            string attributeValue = attribute.InnerText == null ? string.Empty : attribute.InnerText;

            string subMsg = null;

            if (attributeValue.Length < this._minLength)
            {
                subMsg = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_MinLengthConstraintFailed, _minLength);
            }
            else if (attributeValue.Length > this._maxLength)
            {
                subMsg = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_MaxLengthConstraintFailed, _maxLength);
            }

            if (subMsg == null)
            {
                return null;
            }

            return new ValidationErrorInfo()
            {
                Id = "Sem_AttributeValueDataTypeDetailed",
                ErrorType = ValidationErrorType.Schema,
                Node = context.Element,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_AttributeValueDataTypeDetailed,
                                            GetAttributeQualifiedName(context.Element, _attribute), attributeValue, subMsg)
            };
        }
        public override ValidationErrorInfo Validate(ValidationContext context)
        {
            string attributes = string.Empty;
            string existAttribute = string.Empty;
            string existAttribute2 = string.Empty;

            foreach (byte attribute in _attributes)
            {
                attributes += "," + GetAttributeQualifiedName(context.Element, attribute);

                if (context.Element.Attributes[attribute] != null)
                {
                    if (!string.IsNullOrEmpty(existAttribute2))
                    {
                        existAttribute += "," + existAttribute2;
                    }

                    existAttribute2 = GetAttributeQualifiedName(context.Element, attribute).ToString();
                }

            }

            if (string.IsNullOrEmpty(existAttribute))
            {
                return null;
            }

            return new ValidationErrorInfo()
            {
                Id = "Sem_AttributeMutualExclusive",
                ErrorType = ValidationErrorType.Semantic,
                Node = context.Element,
                Description = string.Format(System.Globalization.CultureInfo.CurrentUICulture, ValidationResources.Sem_AttributeMutualExclusive,
                                                        existAttribute.Substring(1), existAttribute2, attributes.Substring(1))
            };
        }
        /// <summary>
        /// Validate QName list in PreserveElements, PreserveAttributes, ProcessContent.
        /// </summary>
        /// <param name="qnameList">The QName list to be validated.</param>
        /// <param name="ignorableNamespaces">The ignorable namespaces.</param>
        /// <param name="validationContext"></param>
        /// <returns>The QName that is not valid.</returns>
        internal static string ValidateQNameList(string qnameList, HashSet<string> ignorableNamespaces, ValidationContext validationContext)
        {
            Debug.Assert(!string.IsNullOrEmpty(qnameList));

            var qnames = new ListValue<StringValue>();
            qnames.InnerText = qnameList;

            foreach (var qname in qnames.Items)
            {
                // must be QName
                var items = qname.Value.Split(':');
                if (items.Length != 2)
                {
                    return qname;
                }

                // Prefix must be already defined.
                var attributeNamesapce = validationContext.Element.LookupNamespace(items[0]);
                if (string.IsNullOrEmpty(attributeNamesapce))
                {
                    return qname;
                }

                // The namespace must be identified by the Ignorable attribute at the same element.
                if (!ignorableNamespaces.Contains(attributeNamesapce))
                {
                    return qname;
                }
            }

            return null;
        }
        /// <summary>
        /// Validate ACB syntax - AlternateContent, Choice, Fallback and their attributes.
        /// </summary>
        /// <param name="validationContext"></param>
        internal static void Validate(ValidationContext validationContext)
        {
            AlternateContent acElement = (AlternateContent)validationContext.Element;

            // Validate MC attribute on AlternateContent
            ValidateMcAttributesOnAcb(validationContext, acElement);

            int status = 0;
            ValidationErrorInfo errorInfo;

            if (acElement.ChildElements.Count == 0)
            {
                // Rule: An AlternateContent element shall contain one or more Choice child elements
                errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_IncompleteContentExpectingComplex", ValidationResources.MC_ShallContainChoice);
                validationContext.EmitError(errorInfo);
            }

            OpenXmlElement child;
            
            child = acElement.GetFirstNonMiscElementChild();

            while (child != null)
            {
                if (child is AlternateContent)
                {
                    // Rule: An AlternateContent element shall not be the child of an AlternateContent element.
                    errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), ValidationResources.MC_ShallNotContainAlternateContent);
                    validationContext.EmitError(errorInfo);

                }
                else
                {
                    switch (status)
                    {
                        case 0:
                            // expect a Choice
                            if (child is AlternateContentChoice)
                            {
                                // validate the MC attributes on Choice
                                ValidateMcAttributesOnAcb(validationContext, child);
                                status = 1;
                            }
                            else
                            {
                                // Rule: An AlternateContent element shall contain one or more Choice child elements
                                errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_IncompleteContentExpectingComplex", ValidationResources.MC_ShallContainChoice);
                                validationContext.EmitError(errorInfo);

                                if (child is AlternateContentFallback)
                                {
                                    // validate the MC attributes on Fallback
                                    ValidateMcAttributesOnAcb(validationContext, child);
                                }
                            }
                            break;

                        case 1:
                            // Already one Choice, expect Choice or Fallback
                            if (child is AlternateContentChoice)
                            {
                                // validate the MC attributes on Choice
                                ValidateMcAttributesOnAcb(validationContext, child);
                                status = 1;
                            }
                            else if (child is AlternateContentFallback)
                            {
                                // validate the MC attributes on Fallback
                                ValidateMcAttributesOnAcb(validationContext, child);
                                status = 2;
                            }
                            else
                            {
                                errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), ValidationResources.MC_ShallContainChoice);
                                validationContext.EmitError(errorInfo);
                            }
                            break;

                        case 2:
                            // Already one Fallback. Can not have more than one Fallback
                            errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), ValidationResources.MC_ShallContainChoice);
                            validationContext.EmitError(errorInfo);
                            break;

                    }
                }
                child = child.GetNextNonMiscElementSibling();
            }
            return;
        }
        /// <summary>
        /// Validate compatibility rule attributes - Ignorable, ProcessContent, PreserveElements, PreserveAttributes, MustUnderstand.
        /// </summary>
        /// <param name="validationContext">The validation context.</param>
        internal static void ValidateMcAttributes(ValidationContext validationContext)
        {
            var element = validationContext.Element;
            if (element.MCAttributes == null)
            {
                return;
            }

            HashSet<string> ignorableNamespaces = null;
            ValidationErrorInfo errorInfo;

            if (element.MCAttributes != null)
            {
                // validate Ignorable attribute
                if (!string.IsNullOrEmpty(element.MCAttributes.Ignorable))
                {
                    ignorableNamespaces = new HashSet<string>();

                    // rule: the prefix must already be defined.
                    var prefixes = new ListValue<StringValue>();
                    prefixes.InnerText = element.MCAttributes.Ignorable;
                    foreach (var prefix in prefixes.Items)
                    {
                        var ignorableNamespace = element.LookupNamespace(prefix);
                        if (string.IsNullOrEmpty(ignorableNamespace))
                        {
                            // error, the prefix is not defined.
                            errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidIgnorableAttribute", element.MCAttributes.Ignorable);
                            validationContext.EmitError(errorInfo);
                        }
                        else
                        {
                            ignorableNamespaces.Add(ignorableNamespace);
                        }
                    }
                }


                // validate PreserveAttributes attribute
                if (!string.IsNullOrEmpty(element.MCAttributes.PreserveAttributes))
                {
                    // The ProcessAttributes attribute value shall not reference any attribute name that does not belong to a namespace
                    // that is identified by the Ignorable attribute of the same element.
                    if (ignorableNamespaces == null)
                    {
                        // must have Ignorable on same element.
                        errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidPreserveAttributesAttribute", element.MCAttributes.PreserveAttributes);
                        validationContext.EmitError(errorInfo);
                    }
                    else
                    {
                        string errorQName = ValidateQNameList(element.MCAttributes.PreserveAttributes, ignorableNamespaces, validationContext);
                        if (!string.IsNullOrEmpty(errorQName))
                        {
                            errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidPreserveAttributesAttribute", element.MCAttributes.PreserveAttributes);
                            validationContext.EmitError(errorInfo);
                        }
                    }
                }

                // validate PreserveElements attribute
                if (!string.IsNullOrEmpty(element.MCAttributes.PreserveElements))
                {
                    // The ProcessAttributes attribute value shall not reference any attribute name that does not belong to a namespace
                    // that is identified by the Ignorable attribute of the same element.
                    if (ignorableNamespaces == null)
                    {
                        // must have Ignorable on same element.
                        errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidPreserveElementsAttribute", element.MCAttributes.PreserveElements);
                        validationContext.EmitError(errorInfo);
                    }
                    else
                    {
                        string errorQName = ValidateQNameList(element.MCAttributes.PreserveElements, ignorableNamespaces, validationContext);
                        if (!string.IsNullOrEmpty(errorQName))
                        {
                            errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidPreserveElementsAttribute", element.MCAttributes.PreserveElements);
                            validationContext.EmitError(errorInfo);
                        }
                    }
                }

                // validate ProcessContent attribute
                if (!string.IsNullOrEmpty(element.MCAttributes.ProcessContent))
                {
                    // The ProcessAttributes attribute value shall not reference any attribute name that does not belong to a namespace
                    // that is identified by the Ignorable attribute of the same element.
                    if (ignorableNamespaces == null)
                    {
                        // must have Ignorable on same element.
                        errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidProcessContentAttribute", element.MCAttributes.ProcessContent);
                        validationContext.EmitError(errorInfo);
                    }
                    else
                    {
                        string errorQName = ValidateQNameList(element.MCAttributes.ProcessContent, ignorableNamespaces, validationContext);
                        if (!string.IsNullOrEmpty(errorQName))
                        {
                            errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidProcessContentAttribute", element.MCAttributes.ProcessContent);
                            validationContext.EmitError(errorInfo);
                        }
                    }

                    foreach (var exAttribute in element.ExtendedAttributes)
                    {
                         // Markup consumers that encounter a non-ignored element that has an xml:lang or xml:space attribute and is also identified by a ProcessContent attribute value might generate an error.
                        if (AlternateContentValidator.IsXmlSpaceOrXmlLangAttribue(exAttribute))
                        {
                            // report error.
                            errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidXmlAttributeWithProcessContent");
                            validationContext.EmitError(errorInfo);
                        }
                    }
                }

                if (!string.IsNullOrEmpty(element.MCAttributes.MustUnderstand))
                {
                    // TODO: MustUnderstand
                    // A markup consumer that does not understand these identified namespaces shall not continue to process the markup document

                    // rule: the prefix must already be defined.
                    var prefixes = new ListValue<StringValue>();
                    prefixes.InnerText = element.MCAttributes.MustUnderstand;
                    foreach (var prefix in prefixes.Items)
                    {
                        var mustunderstandNamespace = element.LookupNamespace(prefix);
                        if (string.IsNullOrEmpty(mustunderstandNamespace))
                        {
                            // report error, the prefix is not defined.
                            errorInfo = validationContext.ComposeMcValidationError(element, "MC_InvalidMustUnderstandAttribute", element.MCAttributes.MustUnderstand);
                            validationContext.EmitError(errorInfo);
                        }
                    }

                }
            }
        }
        /// <summary>
        /// Validate attributes on AlternateContent, Choice and Fallback element.
        /// </summary>
        /// <param name="validationContext"></param>
        /// <param name="acElement">The element to be validated.</param>
        private static void ValidateMcAttributesOnAcb(ValidationContext validationContext, OpenXmlElement acElement)
        {
            ValidationErrorInfo errorInfo;

            // AlternateContent elements might include the attributes Ignorable, MustUnderstand, ProcessContent, PreserveElements, and PreserveAttributes
            // These attributes’ qualified names shall be prefixed when associated with an AlternateContent / Choice / Fallback element.
            // A markup consumer shall generate an error if it encounters an unprefixed attribute name associated with an AlternateContent element.
            if (acElement.ExtendedAttributes != null)
            {
                foreach (var exAttribute in acElement.ExtendedAttributes)
                {
                    if (string.IsNullOrEmpty(exAttribute.Prefix))
                    {
                        // error on any unprefixed attributes
                        errorInfo = validationContext.ComposeMcValidationError(acElement, ValidationResources.MC_ErrorOnUnprefixedAttributeName, exAttribute.XmlQualifiedName.ToString());
                        validationContext.EmitError(errorInfo);
                    }

                    // Markup consumers shall generate an error if they encounter the xml:lang or xml:space attributes on an AlternateContent element.
                    // Markup consumers shall generate an error if they encounter the xml:lang or xml:space attributes on a Choice element, regardless of whether the element is preceded by a selected Choice element.
                    // Markup consumers shall generate an error if they encounter the xml:lang or xml:space attributes on a Fallback element, regardless of whether the element is preceded by a selected Choice element.
                    if (IsXmlSpaceOrXmlLangAttribue(exAttribute))
                    {
                        // report error.
                        errorInfo = validationContext.ComposeMcValidationError(acElement, "MC_InvalidXmlAttribute", acElement.LocalName);
                        validationContext.EmitError(errorInfo);
                    }
                }
            }

            // validate MC attribues (Ignorable, PreserveElements, etc.) of this element.
            CompatibilityRuleAttributesValidator.ValidateMcAttributes(validationContext);


            AlternateContentChoice choice = acElement as AlternateContentChoice;
            if (choice != null)
            {
                // All Choice elements shall have a Requires attribute whose value contains a whitespace-delimited list of namespace prefixes
                if (choice.Requires == null)
                {
                    // report error
                    errorInfo = validationContext.ComposeMcValidationError(acElement, "MC_MissedRequiresAttribute");
                    validationContext.EmitError(errorInfo);
                }
                else
                {
                    var prefixes = new ListValue<StringValue>();
                    prefixes.InnerText = choice.Requires;
                    foreach (var prefix in prefixes.Items)
                    {
                        var ignorableNamespace = choice.LookupNamespace(prefix);
                        if (string.IsNullOrEmpty(ignorableNamespace))
                        {
                            // report error, the prefix is not defined.
                            errorInfo = validationContext.ComposeMcValidationError(choice, "MC_InvalidRequiresAttribute", choice.Requires);
                            validationContext.EmitError(errorInfo);
                        }
                    }
                }
            }

        }
        private List<string> GetReferencedAttributes(ValidationContext context)
        {
            if (_referencedAttributes == null)
            {
                _referencedAttributes = new List<string>();

                OpenXmlPart part = GetReferencedPart(context, _partPath);

                this._relatedPart = part;

                if (part != null)
                {
                    ValidationContext partContext = new ValidationContext();

                    partContext.FileFormat = context.FileFormat;
                    partContext.Package = context.Package;
                    partContext.Part = part;
                    partContext.Element = part.RootElement;

                    ValidationTraverser.ValidatingTraverse(partContext, this.ElementTraverse, null, null);
                }
            }

            return _referencedAttributes;
        }