Skip to content

Commit de35c61

Browse files
authored
Separate Validation code to own files (#345)
1 parent e8065cd commit de35c61

File tree

86 files changed

+3175
-2817
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+3175
-2817
lines changed

DocumentFormat.OpenXml/DocumentFormat.OpenXml.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@
121121
<AutoGen>True</AutoGen>
122122
<DependentUpon>ExceptionMessages.resx</DependentUpon>
123123
</Compile>
124+
<Compile Update="Validation\ValidationResources.Designer.cs">
125+
<DesignTime>True</DesignTime>
126+
<AutoGen>True</AutoGen>
127+
<DependentUpon>ValidationResources.resx</DependentUpon>
128+
</Compile>
124129
</ItemGroup>
125130

126131
<ItemGroup>
@@ -129,6 +134,10 @@
129134
<LastGenOutput>ExceptionMessages.Designer.cs</LastGenOutput>
130135
<CustomToolNamespace>DocumentFormat.OpenXml</CustomToolNamespace>
131136
</EmbeddedResource>
137+
<EmbeddedResource Update="Validation\ValidationResources.resx">
138+
<Generator>ResXFileCodeGenerator</Generator>
139+
<LastGenOutput>ValidationResources.Designer.cs</LastGenOutput>
140+
</EmbeddedResource>
132141
</ItemGroup>
133142

134143
</Project>

DocumentFormat.OpenXml/src/ofapi/Validation/DocumentValidator.cs renamed to DocumentFormat.OpenXml/Validation/DocumentValidator.cs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,6 @@
1111

1212
namespace DocumentFormat.OpenXml.Validation
1313
{
14-
/// <summary>
15-
/// Implement this interface when the operation is cancelable
16-
/// </summary>
17-
internal interface ICancelable
18-
{
19-
/// <summary>
20-
/// On cancel event.
21-
/// </summary>
22-
/// <param name="sender"></param>
23-
/// <param name="eventArgs"></param>
24-
void OnCancel(object sender, EventArgs eventArgs);
25-
}
26-
2714
/// <summary>
2815
/// DocumentValidator - defines a base class for document validator.
2916
/// </summary>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
6+
namespace DocumentFormat.OpenXml.Validation
7+
{
8+
/// <summary>
9+
/// Implement this interface when the operation is cancelable
10+
/// </summary>
11+
internal interface ICancelable
12+
{
13+
/// <summary>
14+
/// On cancel event.
15+
/// </summary>
16+
/// <param name="sender"></param>
17+
/// <param name="eventArgs"></param>
18+
void OnCancel(object sender, EventArgs eventArgs);
19+
}
20+
}

DocumentFormat.OpenXml/src/ofapi/Validation/OpenXmlValidator.cs renamed to DocumentFormat.OpenXml/Validation/OpenXmlValidator.cs

File renamed without changes.

DocumentFormat.OpenXml/src/ofapi/Validation/PresentationDocumentValidator.cs renamed to DocumentFormat.OpenXml/Validation/PresentationDocumentValidator.cs

File renamed without changes.

DocumentFormat.OpenXml/src/ofapi/Validation/SchemaValidation/AllParticleValidator.cs renamed to DocumentFormat.OpenXml/Validation/Schema/AllParticleValidator.cs

File renamed without changes.
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using DocumentFormat.OpenXml.Validation;
5+
6+
namespace DocumentFormat.OpenXml.Internal.SchemaValidation
7+
{
8+
/// <summary>
9+
/// Validator for MarkupCompatibility feature - AlternateContent.
10+
/// </summary>
11+
/// <remarks>
12+
/// See Ecma376 "Part 5: Markup Compatibility and Extensibility" for reference.
13+
/// </remarks>
14+
internal class AlternateContentValidator
15+
{
16+
/// <summary>
17+
/// Validate ACB syntax - AlternateContent, Choice, Fallback and their attributes.
18+
/// </summary>
19+
/// <param name="validationContext"></param>
20+
internal static void Validate(ValidationContext validationContext)
21+
{
22+
AlternateContent acElement = (AlternateContent)validationContext.Element;
23+
24+
// Validate MC attribute on AlternateContent
25+
ValidateMcAttributesOnAcb(validationContext, acElement);
26+
27+
int status = 0;
28+
ValidationErrorInfo errorInfo;
29+
30+
if (acElement.ChildElements.Count == 0)
31+
{
32+
// Rule: An AlternateContent element shall contain one or more Choice child elements
33+
errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_IncompleteContentExpectingComplex", ValidationResources.MC_ShallContainChoice);
34+
validationContext.EmitError(errorInfo);
35+
}
36+
37+
OpenXmlElement child;
38+
39+
child = acElement.GetFirstNonMiscElementChild();
40+
41+
while (child != null)
42+
{
43+
if (child is AlternateContent)
44+
{
45+
// Rule: An AlternateContent element shall not be the child of an AlternateContent element.
46+
errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), ValidationResources.MC_ShallNotContainAlternateContent);
47+
validationContext.EmitError(errorInfo);
48+
}
49+
else
50+
{
51+
switch (status)
52+
{
53+
case 0:
54+
// expect a Choice
55+
if (child is AlternateContentChoice)
56+
{
57+
// validate the MC attributes on Choice
58+
ValidateMcAttributesOnAcb(validationContext, child);
59+
status = 1;
60+
}
61+
else
62+
{
63+
// Rule: An AlternateContent element shall contain one or more Choice child elements
64+
errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_IncompleteContentExpectingComplex", ValidationResources.MC_ShallContainChoice);
65+
validationContext.EmitError(errorInfo);
66+
67+
if (child is AlternateContentFallback)
68+
{
69+
// validate the MC attributes on Fallback
70+
ValidateMcAttributesOnAcb(validationContext, child);
71+
}
72+
}
73+
break;
74+
75+
case 1:
76+
// Already one Choice, expect Choice or Fallback
77+
if (child is AlternateContentChoice)
78+
{
79+
// validate the MC attributes on Choice
80+
ValidateMcAttributesOnAcb(validationContext, child);
81+
status = 1;
82+
}
83+
else if (child is AlternateContentFallback)
84+
{
85+
// validate the MC attributes on Fallback
86+
ValidateMcAttributesOnAcb(validationContext, child);
87+
status = 2;
88+
}
89+
else
90+
{
91+
errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), ValidationResources.MC_ShallContainChoice);
92+
validationContext.EmitError(errorInfo);
93+
}
94+
break;
95+
96+
case 2:
97+
// Already one Fallback. Can not have more than one Fallback
98+
errorInfo = validationContext.ComposeMcValidationError(acElement, "Sch_InvalidElementContentExpectingComplex", child.XmlQualifiedName.ToString(), ValidationResources.MC_ShallContainChoice);
99+
validationContext.EmitError(errorInfo);
100+
break;
101+
}
102+
}
103+
child = child.GetNextNonMiscElementSibling();
104+
}
105+
return;
106+
}
107+
108+
/// <summary>
109+
/// Validate attributes on AlternateContent, Choice and Fallback element.
110+
/// </summary>
111+
/// <param name="validationContext"></param>
112+
/// <param name="acElement">The element to be validated.</param>
113+
private static void ValidateMcAttributesOnAcb(ValidationContext validationContext, OpenXmlElement acElement)
114+
{
115+
ValidationErrorInfo errorInfo;
116+
117+
// AlternateContent elements might include the attributes Ignorable, MustUnderstand, ProcessContent, PreserveElements, and PreserveAttributes
118+
// These attributes’ qualified names shall be prefixed when associated with an AlternateContent / Choice / Fallback element.
119+
// A markup consumer shall generate an error if it encounters an unprefixed attribute name associated with an AlternateContent element.
120+
if (acElement.ExtendedAttributes != null)
121+
{
122+
foreach (var exAttribute in acElement.ExtendedAttributes)
123+
{
124+
if (string.IsNullOrEmpty(exAttribute.Prefix))
125+
{
126+
// error on any unprefixed attributes
127+
errorInfo = validationContext.ComposeMcValidationError(acElement, ValidationResources.MC_ErrorOnUnprefixedAttributeName, exAttribute.XmlQualifiedName.ToString());
128+
validationContext.EmitError(errorInfo);
129+
}
130+
131+
// Markup consumers shall generate an error if they encounter the xml:lang or xml:space attributes on an AlternateContent element.
132+
// 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.
133+
// 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.
134+
if (IsXmlSpaceOrXmlLangAttribue(exAttribute))
135+
{
136+
// report error.
137+
errorInfo = validationContext.ComposeMcValidationError(acElement, "MC_InvalidXmlAttribute", acElement.LocalName);
138+
validationContext.EmitError(errorInfo);
139+
}
140+
}
141+
}
142+
143+
// validate MC attribues (Ignorable, PreserveElements, etc.) of this element.
144+
CompatibilityRuleAttributesValidator.ValidateMcAttributes(validationContext);
145+
146+
AlternateContentChoice choice = acElement as AlternateContentChoice;
147+
if (choice != null)
148+
{
149+
// All Choice elements shall have a Requires attribute whose value contains a whitespace-delimited list of namespace prefixes
150+
if (choice.Requires == null)
151+
{
152+
// report error
153+
errorInfo = validationContext.ComposeMcValidationError(acElement, "MC_MissedRequiresAttribute");
154+
validationContext.EmitError(errorInfo);
155+
}
156+
else
157+
{
158+
var prefixes = new ListValue<StringValue>();
159+
prefixes.InnerText = choice.Requires;
160+
foreach (var prefix in prefixes.Items)
161+
{
162+
var ignorableNamespace = choice.LookupNamespace(prefix);
163+
if (string.IsNullOrEmpty(ignorableNamespace))
164+
{
165+
// report error, the prefix is not defined.
166+
errorInfo = validationContext.ComposeMcValidationError(choice, "MC_InvalidRequiresAttribute", choice.Requires);
167+
validationContext.EmitError(errorInfo);
168+
}
169+
}
170+
}
171+
}
172+
}
173+
174+
/// <summary>
175+
/// Test whether the attribute is "xml:space" or "xml:lang".
176+
/// </summary>
177+
/// <param name="attribute">The attribute to be tested.</param>
178+
/// <returns>True if the attribute is "xml:space" or "xml:lang".</returns>
179+
internal static bool IsXmlSpaceOrXmlLangAttribue(OpenXmlAttribute attribute)
180+
{
181+
if ("http://www.w3.org/XML/1998/namespace" == attribute.NamespaceUri)
182+
{
183+
if (attribute.LocalName == "space" || attribute.LocalName == "lang")
184+
{
185+
return true;
186+
}
187+
}
188+
return false;
189+
}
190+
}
191+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Diagnostics;
5+
6+
namespace DocumentFormat.OpenXml.Internal.SchemaValidation
7+
{
8+
/// <summary>
9+
/// Particle constraint data for particle which type is ParticleType.Any.
10+
/// </summary>
11+
/// <remarks>
12+
/// xsd:any can contains only one namespace.
13+
/// If there are multiple namespace in the original xsd, it will be splitted into multiple xsd:any in binary database.
14+
/// </remarks>
15+
[DebuggerDisplay("NamespaceValue={NamespaceValue}")]
16+
internal class AnyParticle : ParticleConstraint
17+
{
18+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
19+
private ushort _xsdAnyValue;
20+
private IParticleValidator _particleValidator;
21+
22+
/// <summary>
23+
/// Initializes a new instance of the AnyParticle.
24+
/// </summary>
25+
internal AnyParticle()
26+
: base()
27+
{
28+
_particleValidator = new AnyParticleValidator(this);
29+
}
30+
31+
/// <summary>
32+
/// Gets the type of the particle.
33+
/// </summary>
34+
internal override ParticleType ParticleType
35+
{
36+
get { return ParticleType.Any; }
37+
set { Debug.Assert(value == ParticleType.Any); }
38+
}
39+
40+
/// <summary>
41+
/// This field is actually the value of the xsd:any.
42+
/// </summary>
43+
internal override int ElementId
44+
{
45+
set
46+
{
47+
Debug.Assert( value == XsdAnyPrefidefinedValue.Any ||
48+
value == XsdAnyPrefidefinedValue.Local ||
49+
value == XsdAnyPrefidefinedValue.Other ||
50+
value == XsdAnyPrefidefinedValue.TargetNamespace);
51+
52+
this._xsdAnyValue = (ushort)value;
53+
}
54+
}
55+
56+
/// <summary>
57+
/// The value of the xsd:any@namespace.
58+
/// </summary>
59+
internal ushort NamespaceValue
60+
{
61+
get { return this._xsdAnyValue; }
62+
}
63+
64+
/// <summary>
65+
/// Gets a ParticleValidator for this particle constraint.
66+
/// </summary>
67+
internal override IParticleValidator ParticleValidator
68+
{
69+
get { return this._particleValidator; }
70+
}
71+
}
72+
73+
#if false
74+
/// <summary>
75+
/// Particle constraint data for particle which type is ParticleType.All
76+
/// </summary>
77+
internal class AllParticle : CompositeParticle
78+
{
79+
internal AllParticle() : base()
80+
{
81+
}
82+
}
83+
84+
/// <summary>
85+
/// Particle constraint data for particle which type is ParticleType.Choice
86+
/// </summary>
87+
internal class ChoiceParticle : CompositeParticle
88+
{
89+
internal ChoiceParticle()
90+
: base()
91+
{
92+
}
93+
}
94+
95+
/// <summary>
96+
/// Particle constraint data for particle which type is ParticleType.Sequence
97+
/// </summary>
98+
internal class SequenceParticle : CompositeParticle
99+
{
100+
internal SequenceParticle()
101+
: base()
102+
{
103+
}
104+
}
105+
106+
/// <summary>
107+
/// Particle constraint data for particle which type is ParticleType.Group
108+
/// </summary>
109+
internal class GroupParticle : CompositeParticle
110+
{
111+
internal GroupParticle()
112+
: base()
113+
{
114+
}
115+
}
116+
#endif
117+
118+
}

0 commit comments

Comments
 (0)