/// <summary> /// Try match this element. /// </summary> /// <param name="particleMatchInfo"></param> /// <param name="validationContext"></param> public void TryMatch(ParticleMatchInfo particleMatchInfo, ValidationContext validationContext) { Debug.Assert(particleMatchInfo != null); Debug.Assert(particleMatchInfo.StartElement != null); if (this.ElementId != particleMatchInfo.StartElement.ElementTypeId) { particleMatchInfo.Match = ParticleMatch.Nomatch; } else if (this.MaxOccurs == 1) { // matched element once. particleMatchInfo.Match = ParticleMatch.Matched; particleMatchInfo.LastMatchedElement = particleMatchInfo.StartElement; } else { // try to match multiple elements. var element = particleMatchInfo.StartElement; int count = 0; while (element != null && this.MaxOccursGreaterThan(count) && element.ElementTypeId == this.ElementId) { count++; particleMatchInfo.LastMatchedElement = element; element = validationContext.GetNextChildMc(element); } if (count >= this.MinOccurs) { particleMatchInfo.Match = ParticleMatch.Matched; } else { particleMatchInfo.Match = ParticleMatch.Partial; if (validationContext.CollectExpectedChildren) { if (particleMatchInfo.ExpectedChildren == null) { particleMatchInfo.InitExpectedChildren(); } particleMatchInfo.ExpectedChildren.Add(this.ElementId); } } } 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(); this.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, this.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.EmitError(errorInfo); return; } else { expectedChildren = GetExpectedChildrenMessage(validationContext.Element, particleMatchInfo.ExpectedChildren); } break; case ParticleMatch.Matched: if (this.ParticleConstraint.CanOccursMoreThanOne) { expectedChildren = GetExpectedChildrenMessage(validationContext.Element, this.GetExpectedElements()); } else { expectedChildren = GetExpectedChildrenMessage(validationContext.Element, particleMatchInfo.ExpectedChildren); } break; } if (validationContext.Element.CanContainsChild(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 { //Fix bug #448264, specifal case: 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.EmitError(errorInfo); }
/// <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 this.ParticleConstraint.ChildrenParticles) { this._childrenParticles[childParticle.ElementId] = false; } bool visited; while (next != null) { if (this._childrenParticles.TryGetValue(next.ElementTypeId, out visited)) { if (visited) { // error, maxOccurs > 1 break; } else { this._childrenParticles[next.ElementTypeId] = 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 this.ParticleConstraint.ChildrenParticles) { Debug.Assert(childParticle is ElementParticle); particleMatchInfo.ExpectedChildren.Add(childParticle.ElementId); } return; } else { particleMatchInfo.Match = ParticleMatch.Matched; // check if matched foreach (var childParticle in this.ParticleConstraint.ChildrenParticles) { Debug.Assert(childParticle is ElementParticle); if (!this._childrenParticles[childParticle.ElementId] && childParticle.MinOccurs == 1) { // one of the required children are missed. particleMatchInfo.Match = ParticleMatch.Partial; } } // find expected child elements. foreach (var childParticle in this.ParticleConstraint.ChildrenParticles) { if (!this._childrenParticles[childParticle.ElementId]) { particleMatchInfo.ExpectedChildren.Add(childParticle.ElementId); } } return; } }
/// <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: if ( childConstraint.ParticleValidator.GetRequiredElements(null)) { if (validationContext.CollectExpectedChildren) { if (particleMatchInfo.ExpectedChildren == null) { particleMatchInfo.SetExpectedChildren(childConstraint.ParticleValidator.GetRequiredElements()); } else { // reuse same object, avoid object alloction. 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 (this.ParticleConstraint.ChildrenParticles[constraintIndex].ParticleValidator.GetRequiredElements(null)) { if (validationContext.CollectExpectedChildren) { if (particleMatchInfo.ExpectedChildren == null) { particleMatchInfo.InitExpectedChildren(); } this.ParticleConstraint.ChildrenParticles[constraintIndex].ParticleValidator.GetRequiredElements(particleMatchInfo.ExpectedChildren); } particleMatchInfo.Match = ParticleMatch.Partial; return; } } // all other children constraint are optional. particleMatchInfo.Match = ParticleMatch.Matched; return; } }
/// <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: if (childConstraint.ParticleValidator.GetRequiredElements(null)) { if (validationContext.CollectExpectedChildren) { if (particleMatchInfo.ExpectedChildren == null) { particleMatchInfo.SetExpectedChildren(childConstraint.ParticleValidator.GetRequiredElements()); } else { // reuse same object, avoid object alloction. 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 (this.ParticleConstraint.ChildrenParticles[constraintIndex].ParticleValidator.GetRequiredElements(null)) { if (validationContext.CollectExpectedChildren) { if (particleMatchInfo.ExpectedChildren == null) { particleMatchInfo.InitExpectedChildren(); } this.ParticleConstraint.ChildrenParticles[constraintIndex].ParticleValidator.GetRequiredElements(particleMatchInfo.ExpectedChildren); } particleMatchInfo.Match = ParticleMatch.Partial; return; } } // all other children constraint are optional. particleMatchInfo.Match = ParticleMatch.Matched; return; } }