Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/DocumentFormat.OpenXml.Framework/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Linq;

namespace DocumentFormat.OpenXml;

internal static class EnumerableExtensions
{
/// <summary>
/// Similar to <see cref="Enumerable.FirstOrDefault{TSource}(IEnumerable{TSource})"/> but will also verify that at most there is one.
/// </summary>
public static T? FirstOrDefaultAndMaxOne<T>(this IEnumerable<T> enumerable, Func<Exception>? exceptionFactory = null)
{
using var e = enumerable.GetEnumerator();

if (e.MoveNext())
{
var first = e.Current;

if (e.MoveNext())
{
throw exceptionFactory?.Invoke() ?? throw new InvalidOperationException(ExceptionMessages.FirstOrDefaultMaxOne);
}

return first;
}

return default;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -411,4 +411,7 @@
<data name="FailedToOpenPackage" xml:space="preserve">
<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>
</data>
<data name="FirstOrDefaultMaxOne" xml:space="preserve">
<value>The enumerable contained more than a single element when only zero or one are allowed.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public RelationshipTypeConstraint(OpenXmlQualifiedName attribute, string type)
_type = type;
}

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

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

if (!rels.Any())
if (rels.FirstOrDefault() is { } rel)
{
var pairs = current.Part.Parts.Where(p => p.RelationshipId == attribute.Value.InnerText);

if (pairs.Any())
{
Debug.Assert(pairs.Count() == 1);
actualType = pairs.First().OpenXmlPart.RelationshipType;
}
actualType = rel.RelationshipType;
}
else
{
Debug.Assert(rels.Count() == 1);
actualType = rels.First().RelationshipType;
var pair = current.Part.Parts
.Where(p => p.RelationshipId == attribute.Value.InnerText)
.FirstOrDefaultAndMaxOne();

if (pair is { })
{
actualType = pair.OpenXmlPart.RelationshipType;
}
}

if (actualType == _type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace DocumentFormat.OpenXml.Validation.Semantic
/// <summary>
/// Base class for each semantic constraint category.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1851:Possible multiple enumerations of 'IEnumerable' collection", Justification = "https://github.com/dotnet/Open-XML-SDK/issues/1325")]
internal abstract class SemanticConstraint : IValidator
{
public SemanticConstraint(SemanticValidationLevel level)
Expand Down Expand Up @@ -130,13 +129,10 @@ private static void Get(ValidationContext context, out SemanticValidationLevel l
}
else if (parts[0] == "..")
{
var refParts = current.Package
return current.Package
.GetAllParts()
.Where(p => p.Parts.Any(r => r.OpenXmlPart.PackagePart.Uri == current.Part.PackagePart.Uri));

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

return refParts.First();
.Where(p => p.Parts.Any(r => r.OpenXmlPart.PackagePart.Uri == current.Part.PackagePart.Uri))
.First();
}
else
{
Expand Down Expand Up @@ -247,29 +243,25 @@ protected static bool GetAttrNumVal(OpenXmlSimpleType attributeValue, out double

private static OpenXmlPart? GetPartThroughPartPath(IEnumerable<IdPartPair> pairs, string[] path)
{
var temp = default(OpenXmlPart);
var foundPart = default(OpenXmlPart);
var parts = pairs;

for (int i = 0; i < path.Length; i++)
{
var s = parts.Where(p => p.OpenXmlPart.GetType().Name == path[i]).Select(t => t.OpenXmlPart);
var count = s.Count();

if (count > 1)
{
throw new System.IO.FileFormatException(ValidationResources.MoreThanOnePartForOneUri);
}
foundPart = parts
.Where(p => p.OpenXmlPart.GetType().Name == path[i])
.Select(t => t.OpenXmlPart)
.FirstOrDefaultAndMaxOne(static () => new System.IO.FileFormatException(ValidationResources.MoreThanOnePartForOneUri));

if (count == 0)
if (foundPart is not { })
{
return null;
}

temp = s.First();
parts = temp.Parts;
parts = foundPart.Parts;
}

return temp;
return foundPart;
}

protected readonly struct PartHolder<T>
Expand Down
Loading