Skip to content

Commit d596354

Browse files
authored
Refactor to clean up CA1851 warnings (#1892)
Fixes #1325
1 parent dc15de0 commit d596354

File tree

5 files changed

+66
-30
lines changed

5 files changed

+66
-30
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
using System.Collections.Generic;
6+
using System.Linq;
7+
8+
namespace DocumentFormat.OpenXml;
9+
10+
internal static class EnumerableExtensions
11+
{
12+
/// <summary>
13+
/// Similar to <see cref="Enumerable.FirstOrDefault{TSource}(IEnumerable{TSource})"/> but will also verify that at most there is one.
14+
/// </summary>
15+
public static T? FirstOrDefaultAndMaxOne<T>(this IEnumerable<T> enumerable, Func<Exception>? exceptionFactory = null)
16+
{
17+
using var e = enumerable.GetEnumerator();
18+
19+
if (e.MoveNext())
20+
{
21+
var first = e.Current;
22+
23+
if (e.MoveNext())
24+
{
25+
throw exceptionFactory?.Invoke() ?? throw new InvalidOperationException(ExceptionMessages.FirstOrDefaultMaxOne);
26+
}
27+
28+
return first;
29+
}
30+
31+
return default;
32+
}
33+
}

src/DocumentFormat.OpenXml.Framework/Resources/ExceptionMessages.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/DocumentFormat.OpenXml.Framework/Resources/ExceptionMessages.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,4 +411,7 @@
411411
<data name="FailedToOpenPackage" xml:space="preserve">
412412
<value>Package could not be opened for stream. See inner exception for details and be aware that there are behavior differences in stream support between .NET Framework and Core.</value>
413413
</data>
414+
<data name="FirstOrDefaultMaxOne" xml:space="preserve">
415+
<value>The enumerable contained more than a single element when only zero or one are allowed.</value>
416+
</data>
414417
</root>

src/DocumentFormat.OpenXml.Framework/Validation/Semantic/RelationshipTypeConstraint.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ public RelationshipTypeConstraint(OpenXmlQualifiedName attribute, string type)
2121
_type = type;
2222
}
2323

24-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1851:Possible multiple enumerations of 'IEnumerable' collection", Justification = "https://github.com/dotnet/Open-XML-SDK/issues/1325")]
2524
public override ValidationErrorInfo? ValidateCore(ValidationContext context)
2625
{
2726
var current = context.Stack.Current;
@@ -57,20 +56,20 @@ public RelationshipTypeConstraint(OpenXmlQualifiedName attribute, string type)
5756

5857
var rels = current.Part.ExternalRelationships.Where(r => r.Id == attribute.Value.InnerText);
5958

60-
if (!rels.Any())
59+
if (rels.FirstOrDefault() is { } rel)
6160
{
62-
var pairs = current.Part.Parts.Where(p => p.RelationshipId == attribute.Value.InnerText);
63-
64-
if (pairs.Any())
65-
{
66-
Debug.Assert(pairs.Count() == 1);
67-
actualType = pairs.First().OpenXmlPart.RelationshipType;
68-
}
61+
actualType = rel.RelationshipType;
6962
}
7063
else
7164
{
72-
Debug.Assert(rels.Count() == 1);
73-
actualType = rels.First().RelationshipType;
65+
var pair = current.Part.Parts
66+
.Where(p => p.RelationshipId == attribute.Value.InnerText)
67+
.FirstOrDefaultAndMaxOne();
68+
69+
if (pair is { })
70+
{
71+
actualType = pair.OpenXmlPart.RelationshipType;
72+
}
7473
}
7574

7675
if (actualType == _type)

src/DocumentFormat.OpenXml.Framework/Validation/Semantic/SemanticConstraint.cs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ namespace DocumentFormat.OpenXml.Validation.Semantic
1616
/// <summary>
1717
/// Base class for each semantic constraint category.
1818
/// </summary>
19-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1851:Possible multiple enumerations of 'IEnumerable' collection", Justification = "https://github.com/dotnet/Open-XML-SDK/issues/1325")]
2019
internal abstract class SemanticConstraint : IValidator
2120
{
2221
public SemanticConstraint(SemanticValidationLevel level)
@@ -130,13 +129,10 @@ private static void Get(ValidationContext context, out SemanticValidationLevel l
130129
}
131130
else if (parts[0] == "..")
132131
{
133-
var refParts = current.Package
132+
return current.Package
134133
.GetAllParts()
135-
.Where(p => p.Parts.Any(r => r.OpenXmlPart.PackagePart.Uri == current.Part.PackagePart.Uri));
136-
137-
Debug.Assert(refParts.Count() == 1);
138-
139-
return refParts.First();
134+
.Where(p => p.Parts.Any(r => r.OpenXmlPart.PackagePart.Uri == current.Part.PackagePart.Uri))
135+
.First();
140136
}
141137
else
142138
{
@@ -247,29 +243,25 @@ protected static bool GetAttrNumVal(OpenXmlSimpleType attributeValue, out double
247243

248244
private static OpenXmlPart? GetPartThroughPartPath(IEnumerable<IdPartPair> pairs, string[] path)
249245
{
250-
var temp = default(OpenXmlPart);
246+
var foundPart = default(OpenXmlPart);
251247
var parts = pairs;
252248

253249
for (int i = 0; i < path.Length; i++)
254250
{
255-
var s = parts.Where(p => p.OpenXmlPart.GetType().Name == path[i]).Select(t => t.OpenXmlPart);
256-
var count = s.Count();
257-
258-
if (count > 1)
259-
{
260-
throw new System.IO.FileFormatException(ValidationResources.MoreThanOnePartForOneUri);
261-
}
251+
foundPart = parts
252+
.Where(p => p.OpenXmlPart.GetType().Name == path[i])
253+
.Select(t => t.OpenXmlPart)
254+
.FirstOrDefaultAndMaxOne(static () => new System.IO.FileFormatException(ValidationResources.MoreThanOnePartForOneUri));
262255

263-
if (count == 0)
256+
if (foundPart is not { })
264257
{
265258
return null;
266259
}
267260

268-
temp = s.First();
269-
parts = temp.Parts;
261+
parts = foundPart.Parts;
270262
}
271263

272-
return temp;
264+
return foundPart;
273265
}
274266

275267
protected readonly struct PartHolder<T>

0 commit comments

Comments
 (0)