Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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>? exThrow = null)
{
using var e = enumerable.GetEnumerator();

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

if (e.MoveNext())
{
throw exThrow?.Invoke() ?? throw new InvalidOperationException("Max of a single item should be in the enumerable");
}

return first;
}

return default;
}
}
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