Ejemplo n.º 1
0
 /// <summary>
 /// Try match the particle.
 /// </summary>
 /// <param name="particleMatchInfo"></param>
 /// <param name="validationContext">The context information for validation.</param>
 public virtual void TryMatch(ParticleMatchInfo particleMatchInfo, ValidationContext validationContext)
 {
     particleMatchInfo.Match = ParticleMatch.Nomatch;
     return;
 }
        protected virtual void EmitInvalidElementError(ValidationContext validationContext,
                                                       ParticleMatchInfo particleMatchInfo)
        {
            OpenXmlElement child;

            // re-validate the element, collect the expected children information
            if (particleMatchInfo.Match != ParticleMatch.Nomatch)
            {
#if DEBUG
                var oldParticleMatchInfo = particleMatchInfo;
                particleMatchInfo = new ParticleMatchInfo();
#endif
                child = validationContext.GetFirstChildMc();
                validationContext.CollectExpectedChildren = true;
                particleMatchInfo.Reset(child);
                particleMatchInfo.InitExpectedChildren();
                TryMatch(particleMatchInfo, validationContext);
                validationContext.CollectExpectedChildren = false;

#if DEBUG
                Debug.Assert(particleMatchInfo.Match == oldParticleMatchInfo.Match);
                Debug.Assert(particleMatchInfo.LastMatchedElement == oldParticleMatchInfo.LastMatchedElement);
#endif
            }

            var element = validationContext.Element;
            if (particleMatchInfo.LastMatchedElement == null)
            {
                child = validationContext.GetFirstChildMc();
            }
            else
            {
                child = validationContext.GetNextChildMc(particleMatchInfo.LastMatchedElement);
            }

            ValidationErrorInfo errorInfo;
            string expectedChildren = null;

            switch (particleMatchInfo.Match)
            {
            case ParticleMatch.Nomatch:
                expectedChildren = GetExpectedChildrenMessage(validationContext.Element, GetExpectedElements());
                break;

            case ParticleMatch.Partial:
                // error: the child can not be matched, it is invalid
                if (child == null)
                {
                    // missing child
                    errorInfo = validationContext.ComposeSchemaValidationError(element, null, "Sch_IncompleteContentExpectingComplex", GetExpectedChildrenMessage(element, particleMatchInfo.ExpectedChildren));
                    validationContext.AddError(errorInfo);

                    return;
                }
                else
                {
                    expectedChildren = GetExpectedChildrenMessage(validationContext.Element, particleMatchInfo.ExpectedChildren);
                }

                break;

            case ParticleMatch.Matched:
                if (ParticleConstraint.CanOccursMoreThanOne)
                {
                    expectedChildren = GetExpectedChildrenMessage(validationContext.Element, GetExpectedElements());
                }
                else
                {
                    expectedChildren = GetExpectedChildrenMessage(validationContext.Element, particleMatchInfo.ExpectedChildren);
                }

                break;
            }

            if (validationContext.Element.CanContainChild(child))
            {
                // The child can be contained in the parent, but not follow the schema.
                errorInfo = validationContext.ComposeSchemaValidationError(element, child, "Sch_UnexpectedElementContentExpectingComplex", child.XmlQualifiedName.ToString(), expectedChildren);
            }
            else
            {
                // Same element name, but wrong type. Only occurs when validating memory DOM.
                var validElement = element.TryCreateValidChild(validationContext.FileFormat, child.NamespaceUri, child.LocalName);
                if (validElement == null)
                {
                    errorInfo = validationContext.ComposeSchemaValidationError(element, child, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), expectedChildren);
                }
                else
                {
                    // Parent can contains a different type of element with same name
                    errorInfo = validationContext.ComposeSchemaValidationError(element, child, "Sch_InvalidElementContentWrongType", child.XmlQualifiedName.ToString(), child.GetType().Name);
                }
            }

            validationContext.AddError(errorInfo);
        }
        /// <summary>
        /// Be called on root particle of complex type.
        /// </summary>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        internal override void Validate(ValidationContext validationContext)
        {
            Debug.Assert(validationContext != null);

            OpenXmlCompositeElement element = validationContext.Element as OpenXmlCompositeElement;

            Debug.Assert(element != null);

            var child = validationContext.GetFirstChildMc();
            ValidationErrorInfo errorInfo;

            // no children
            if (child == null)
            {
                if (ParticleConstraint.MinOccurs == 0)
                {
                    // no child, OK
                    return;
                }
                else
                {
                    var requiredElements = GetRequiredElements();

                    if (requiredElements.Count > 0)
                    {
                        errorInfo = validationContext.ComposeSchemaValidationError(element, null, "Sch_IncompleteContentExpectingComplex", GetExpectedChildrenMessage(element, requiredElements));
                        validationContext.AddError(errorInfo);
                    }

                    return;
                }
            }

            if (_particleMatchInfo == null)
            {
                _particleMatchInfo = new ParticleMatchInfo(child);
            }
            else
            {
                _particleMatchInfo.Reset(child);
            }

            TryMatch(_particleMatchInfo, validationContext);

            switch (_particleMatchInfo.Match)
            {
            case ParticleMatch.Nomatch:
                // error: can not be matched, it is invalid
                EmitInvalidElementError(validationContext, _particleMatchInfo);
                return;

            case ParticleMatch.Partial:
                EmitInvalidElementError(validationContext, _particleMatchInfo);
                return;

            case ParticleMatch.Matched:
                Debug.Assert(_particleMatchInfo.LastMatchedElement != null);
                child = validationContext.GetNextChildMc(_particleMatchInfo.LastMatchedElement);
                {
                    // Two cases now.
                    // 1. All children be matched.
                    // 2. Too many children ( > maxOccurs ).
                    if (child != null)
                    {
                        // invalid child
                        EmitInvalidElementError(validationContext, _particleMatchInfo);

                        // TODO: how can we tell the user what is the required child? Use reflection in OpenXmlElement.
                    }
                    else
                    {
                        //Debug.Assert(result.Valid == true);
                    }
                }

                break;
            }

            return;
        }
        /// <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)
        {
            if (ParticleConstraint.MaxOccurs == 1)
            {
                TryMatchOnce(particleMatchInfo, validationContext);
            }
            else
            {
                int matchCount = 0;
                var next       = particleMatchInfo.StartElement;

                while (next != null && ParticleConstraint.MaxOccursGreaterThan(matchCount))
                {
                    // Use Reset() instead of new() to avoid heavy memory allocation and GC.
                    _childMatchInfo.Reset(next);
                    TryMatchOnce(_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);

                    if (_childMatchInfo.Match == ParticleMatch.Nomatch)
                    {
                        break;
                    }
                    else if (_childMatchInfo.Match == ParticleMatch.Matched)
                    {
                        matchCount++;
                        particleMatchInfo.LastMatchedElement = _childMatchInfo.LastMatchedElement;
                        next = validationContext.GetNextChildMc(particleMatchInfo.LastMatchedElement);
                    }
                    else
                    {
                        // return error
                        particleMatchInfo.Match = ParticleMatch.Partial;
                        particleMatchInfo.LastMatchedElement = _childMatchInfo.LastMatchedElement;
                        if (validationContext.CollectExpectedChildren)
                        {
                            particleMatchInfo.SetExpectedChildren(_childMatchInfo.ExpectedChildren);
                        }

                        return;
                    }
                }

                if (matchCount == 0)
                {
                    particleMatchInfo.Match = ParticleMatch.Nomatch;
                    if (validationContext.CollectExpectedChildren)
                    {
                        particleMatchInfo.SetExpectedChildren(GetExpectedElements());
                    }
                }
                else if (matchCount >= ParticleConstraint.MinOccurs)
                {
                    // matched OK
                    particleMatchInfo.Match = ParticleMatch.Matched;
                }
                else
                {
                    if (GetRequiredElements(particleMatchInfo.ExpectedChildren))
                    {
                        // minOccurs failed, incomplete children.
                        particleMatchInfo.Match = ParticleMatch.Partial;
                    }
                    else
                    {
                        // all children elements are optional
                        particleMatchInfo.Match = ParticleMatch.Matched;
                    }
                }
            }

            return;
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Try match this element once.
        /// </summary>
        /// <param name="particleMatchInfo"></param>
        /// <param name="validationContext">The context information for validation.</param>
        public virtual void TryMatchOnce(ParticleMatchInfo particleMatchInfo, ValidationContext validationContext)
        {
            Debug.Assert(particleMatchInfo != null);
            Debug.Assert(particleMatchInfo.StartElement != null);
            Debug.Assert(!(particleMatchInfo.StartElement is OpenXmlMiscNode));

            var element = particleMatchInfo.StartElement;

            switch (_particleConstraint.NamespaceValue)
            {
            case XsdAnyPrefidefinedValue.Any:
                // Elements from any namespace can be present.
                particleMatchInfo.Match = ParticleMatch.Matched;
                particleMatchInfo.LastMatchedElement = element;
                return;

            case XsdAnyPrefidefinedValue.Local:
                // Elements that are not qualified with a namespace can be present.
                if (String.IsNullOrEmpty(element.NamespaceUri))
                {
                    particleMatchInfo.Match = ParticleMatch.Matched;
                    particleMatchInfo.LastMatchedElement = element;
                    return;
                }
                else
                {
                    particleMatchInfo.Match = ParticleMatch.Nomatch;
                    return;
                }

            case XsdAnyPrefidefinedValue.Other:
                // Elements from any namespace that is not the target namespace of the parent element containing this element can be present.
                if (String.IsNullOrEmpty(element.NamespaceUri) ||
                    element.Parent != null && element.NamespaceUri != element.Parent.NamespaceUri)
                {
                    particleMatchInfo.Match = ParticleMatch.Matched;
                    particleMatchInfo.LastMatchedElement = element;
                    return;
                }
                else
                {
                    particleMatchInfo.Match = ParticleMatch.Nomatch;
                    return;
                }

            case XsdAnyPrefidefinedValue.TargetNamespace:
                if (element.Parent != null && element.NamespaceUri == element.Parent.NamespaceUri)
                {
                    particleMatchInfo.Match = ParticleMatch.Matched;
                    particleMatchInfo.LastMatchedElement = element;
                    return;
                }
                else
                {
                    particleMatchInfo.Match = ParticleMatch.Nomatch;
                    return;
                }
            }

            return;
        }
Ejemplo n.º 6
0
        /// <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 = ParticleConstraint.ChildrenParticles.Length;

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

                // Use Reset() instead of new() to avoid heavy memory allocation 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:
                    if (childConstraint.ParticleValidator.GetRequiredElements(null))
                    {
                        if (validationContext.CollectExpectedChildren)
                        {
                            if (particleMatchInfo.ExpectedChildren == null)
                            {
                                particleMatchInfo.SetExpectedChildren(childConstraint.ParticleValidator.GetRequiredElements());
                            }
                            else
                            {
                                // reuse same object, avoid object allocation.
                                particleMatchInfo.ExpectedChildren.Clear();
                                childConstraint.ParticleValidator.GetRequiredElements(particleMatchInfo.ExpectedChildren);
                            }
                        }

                        // incomplete children.
                        if (next == particleMatchInfo.StartElement)
                        {
                            // the first child is not the correct one.
                            particleMatchInfo.Match = ParticleMatch.Nomatch;
                            particleMatchInfo.LastMatchedElement = null;
                            return;
                        }
                        else
                        {
                            // partial match, incomplete children.
                            particleMatchInfo.Match = ParticleMatch.Partial;
                            return;
                        }
                    }
                    else
                    {
                        // continue trying match next child constraint.
                        constraintIndex++;
                        continue;
                    }

                case ParticleMatch.Matched:
                    particleMatchInfo.LastMatchedElement = _childMatchInfo.LastMatchedElement;
                    next = validationContext.GetNextChildMc(particleMatchInfo.LastMatchedElement);

                    // continue trying match next child constraint.
                    constraintIndex++;
                    break;

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

                    return;
                }
            }

            if (constraintIndex == constraintTotal)
            {
                if (particleMatchInfo.LastMatchedElement != null)
                {
                    particleMatchInfo.Match = ParticleMatch.Matched;
                }
                else
                {
                    particleMatchInfo.Match = ParticleMatch.Nomatch;
                }

                return;
            }
            else
            {
                for (; constraintIndex < constraintTotal; constraintIndex++)
                {
                    if (ParticleConstraint.ChildrenParticles[constraintIndex].ParticleValidator.GetRequiredElements(null))
                    {
                        if (validationContext.CollectExpectedChildren)
                        {
                            if (particleMatchInfo.ExpectedChildren == null)
                            {
                                particleMatchInfo.InitExpectedChildren();
                            }

                            ParticleConstraint.ChildrenParticles[constraintIndex].ParticleValidator.GetRequiredElements(particleMatchInfo.ExpectedChildren);
                        }

                        particleMatchInfo.Match = ParticleMatch.Partial;
                        return;
                    }
                }

                // all other children constraint are optional.
                particleMatchInfo.Match = ParticleMatch.Matched;
                return;
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Try match the particle once.
        /// </summary>
        /// <param name="particleMatchInfo"></param>
        /// <param name="validationContext">The context information for validation.</param>
        /// <remarks>
        /// xsd:all can only contain xsd:element children and maxOccurs of each children can only be 1
        /// </remarks>
        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;

            foreach (var childParticle in ParticleConstraint.ChildrenParticles)
            {
                if (childParticle is ElementParticle element)
                {
                    _childrenParticles[element.ElementType] = false;
                }
            }

            while (next != null)
            {
                if (_childrenParticles.TryGetValue(next.GetType(), out bool visited))
                {
                    if (visited)
                    {
                        // error, maxOccurs > 1
                        break;
                    }
                    else
                    {
                        _childrenParticles[next.GetType()] = true;
                    }
                }
                else
                {
                    // error, other child not allowed
                    break;
                }

                particleMatchInfo.LastMatchedElement = next;

                next = validationContext.GetNextChildMc(next);
            }

            if (particleMatchInfo.ExpectedChildren == null)
            {
                particleMatchInfo.InitExpectedChildren();
            }

            if (particleMatchInfo.LastMatchedElement == null)
            {
                Debug.Assert(next == particleMatchInfo.StartElement);
                particleMatchInfo.Match = ParticleMatch.Nomatch;

                foreach (var childParticle in ParticleConstraint.ChildrenParticles)
                {
                    if (childParticle is ElementParticle element)
                    {
                        particleMatchInfo.ExpectedChildren.Add(element.ElementType);
                    }
                }

                return;
            }
            else
            {
                particleMatchInfo.Match = ParticleMatch.Matched;

                // check if matched
                foreach (var childParticle in ParticleConstraint.ChildrenParticles)
                {
                    if (childParticle is ElementParticle element)
                    {
                        if (!_childrenParticles[element.ElementType] && childParticle.MinOccurs == 1)
                        {
                            // one of the required children are missed.
                            particleMatchInfo.Match = ParticleMatch.Partial;
                        }
                    }
                }

                // find expected child elements.
                foreach (var childParticle in ParticleConstraint.ChildrenParticles)
                {
                    if (childParticle is ElementParticle element && !_childrenParticles[element.ElementType])
                    {
                        particleMatchInfo.ExpectedChildren.Add(element.ElementType);
                    }
                }

                return;
            }
        }
Ejemplo n.º 8
0
        protected override void EmitInvalidElementError(ValidationContext validationContext, ParticleMatchInfo particleMatchInfo)
        {
            var            element = validationContext.Stack.Current.Element;
            OpenXmlElement child;

            if (particleMatchInfo.LastMatchedElement == null)
            {
                child = validationContext.GetFirstChildMc();
            }
            else
            {
                child = validationContext.GetNextChildMc(particleMatchInfo.LastMatchedElement);
            }

            string expectedChildren;
            ValidationErrorInfo errorInfo;

            switch (particleMatchInfo.Match)
            {
            case ParticleMatch.Nomatch:
                expectedChildren = GetExpectedChildrenMessage(validationContext.Stack.Current.Element, GetExpectedElements());
                errorInfo        = validationContext.ComposeSchemaValidationError(element, child, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), expectedChildren);
                validationContext.AddError(errorInfo);
                break;

            case ParticleMatch.Partial:
            case ParticleMatch.Matched:
                if (_childrenParticles.ContainsKey(child.GetType()))
                {
                    // more than one occurs of a child.
                    errorInfo = validationContext.ComposeSchemaValidationError(element, child, "Sch_AllElement", child.XmlQualifiedName.ToString());
                    validationContext.AddError(errorInfo);
                }
                else
                {
                    expectedChildren = GetExpectedChildrenMessage(validationContext.Stack.Current.Element, particleMatchInfo.ExpectedChildren);
                    errorInfo        = validationContext.ComposeSchemaValidationError(element, child, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), expectedChildren);
                    validationContext.AddError(errorInfo);
                }

                break;
            }
        }