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
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace DocumentFormat.OpenXml.Builder;
/// <summary>
/// A delegate for initializing a package.
/// </summary>
internal delegate void PackageInitializerDelegate<TPackage>(TPackage package);
internal delegate void PackageDelegate<TPackage>(TPackage package);

/// <summary>
/// Defines a builder to create an initialization pipeline for a <typeparamref name="TPackage"/>.
Expand All @@ -32,7 +32,7 @@ internal interface IPackageBuilder<TPackage>
/// </summary>
/// <param name="configure">The middleware to add.</param>
/// <returns>The <see cref="IPackageBuilder{TPackage}"/>.</returns>
IPackageBuilder<TPackage> Use(Func<PackageInitializerDelegate<TPackage>, PackageInitializerDelegate<TPackage>> configure);
IPackageBuilder<TPackage> Use(Func<PackageDelegate<TPackage>, PackageDelegate<TPackage>> configure);

/// <summary>
/// Create a copy of the builder that will be independent of the original, but retains the existing middleware and properties.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ internal interface IPackageFactory<TPackage>
/// <param name="initializer">Initializer for the package.</param>
/// <returns>The created package.</returns>
TPackage Create(IPackageInitializer initializer);

PackageDelegate<TPackage>? Template { get; set; }

IPackageFactory<TPackage> New();
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ internal abstract class OpenXmlPackageBuilder<TPackage> : IPackageBuilder<TPacka
where TPackage : OpenXmlPackage
{
private Dictionary<string, object?>? _properties;
private PackageInitializerDelegate<TPackage>? _pipeline;
private PackageDelegate<TPackage>? _pipeline;
private bool _isLocked;
private List<Func<PackageInitializerDelegate<TPackage>, PackageInitializerDelegate<TPackage>>>? _middleware;
private List<Func<PackageDelegate<TPackage>, PackageDelegate<TPackage>>>? _middleware;

public IDictionary<string, object?> Properties => _properties ??= new();

Expand All @@ -37,7 +37,7 @@ internal OpenXmlPackageBuilder(OpenXmlPackageBuilder<TPackage>? parent)
}
}

public IPackageBuilder<TPackage> Use(Func<PackageInitializerDelegate<TPackage>, PackageInitializerDelegate<TPackage>> configure)
public IPackageBuilder<TPackage> Use(Func<PackageDelegate<TPackage>, PackageDelegate<TPackage>> configure)
{
if (_isLocked)
{
Expand All @@ -58,7 +58,7 @@ public IPackageBuilder<TPackage> Use(Func<PackageInitializerDelegate<TPackage>,
public IPackageFactory<TPackage> Build() => new Factory(Create, BuildPipeline());

[MemberNotNull(nameof(_pipeline))]
private PackageInitializerDelegate<TPackage> BuildPipeline()
private PackageDelegate<TPackage> BuildPipeline()
{
_isLocked = true;

Expand All @@ -68,11 +68,11 @@ private PackageInitializerDelegate<TPackage> BuildPipeline()
}

var factory = new PackageFactoryFeature(Clone());
var pipeline = new PackageInitializerDelegate<TPackage>(factory.PipelineTerminator);
var pipeline = new PackageDelegate<TPackage>(factory.PipelineTerminator);

if (_middleware is not null)
{
for (int i = _middleware.Count - 1; i >= 0; i--)
for (var i = _middleware.Count - 1; i >= 0; i--)
{
pipeline = _middleware[i](pipeline);
}
Expand All @@ -84,20 +84,27 @@ private PackageInitializerDelegate<TPackage> BuildPipeline()
private sealed class Factory : IPackageFactory<TPackage>
{
private readonly Func<TPackage> _package;
private readonly PackageInitializerDelegate<TPackage> _pipeline;
private readonly PackageDelegate<TPackage> _pipeline;

public Factory(Func<TPackage> package, PackageInitializerDelegate<TPackage> pipeline)
public Factory(Func<TPackage> package, PackageDelegate<TPackage> pipeline)
{
_package = package;
_pipeline = pipeline;
}

public PackageDelegate<TPackage>? Template { get; set; }

public IPackageFactory<TPackage> New() => new Factory(_package, _pipeline);

public TPackage Create(IPackageInitializer initializer)
{
var package = _package();

initializer.Initialize(package);

package.Features.Set<TemplateFeature>(new TemplateFeature(Template));
_pipeline(package);
package.Features.Set<TemplateFeature>(null);

return package;
}
Expand All @@ -114,6 +121,23 @@ private sealed class PackageFactoryFeature : IPackageFactoryFeature<TPackage>
public void PipelineTerminator(TPackage package)
{
package.Features.Set<IPackageFactoryFeature<TPackage>>(this);

if (package.Features.Get<TemplateFeature>() is { } feature)
{
feature.Initialize(package);
}
}
}

private sealed class TemplateFeature
{
private readonly PackageDelegate<TPackage>? _initializer;

public TemplateFeature(PackageDelegate<TPackage>? initializer)
{
_initializer = initializer;
}

public void Initialize(TPackage package) => _initializer?.Invoke(package);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ internal static TPackage Open<TPackage>(this IPackageFactory<TPackage> builder)
where TPackage : OpenXmlPackage
=> builder.Create(new StreamPackageFeature(new MemoryStream(), PackageOpenMode.Create));

internal static TPackage Use<TPackage>(this TPackage package, PackageInitializerDelegate<TPackage> action)
internal static TPackage Use<TPackage>(this TPackage package, PackageDelegate<TPackage> action)
where TPackage : OpenXmlPackage
{
action(package);
return package;
}

internal static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPackage> builder, PackageInitializerDelegate<TPackage> action)
internal static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPackage> builder, PackageDelegate<TPackage> action)
where TPackage : OpenXmlPackage
=> builder.Use((package, next) =>
{
Expand All @@ -92,7 +92,7 @@ internal static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPa
/// <summary>
/// Adds the <paramref name="middleware"/> to the builder for initializing a package.
/// </summary>
public static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPackage> builder, Action<TPackage, PackageInitializerDelegate<TPackage>> middleware)
public static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPackage> builder, Action<TPackage, PackageDelegate<TPackage>> middleware)
where TPackage : OpenXmlPackage
{
if (builder is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,99 +16,30 @@ namespace DocumentFormat.OpenXml.Builder;
[Obsolete(ExperimentalApis.Message, DiagnosticId = ExperimentalApis.PackageBuilder, UrlFormat = ExperimentalApis.UrlFormat)]
internal static class TemplateBuilderExtensions
{
/// <summary>
/// Adds a template to the current <paramref name="builder"/>.
/// </summary>
public static IPackageBuilder<TPackage> UseTemplate<TPackage, TType>(this IPackageBuilder<TPackage> builder, string path, TType type)
public static IPackageFactory<TPackage> WithTemplate<TPackage, TType>(this IPackageFactory<TPackage> packageFactory, string path, TType type)
where TPackage : OpenXmlPackage
where TType : struct
=> builder.CreateTemplateBuilder(
builder => builder.Open(path, PackageOpenMode.Read),
package =>
{
var typeFeature = package.Features.GetRequired<IDocumentTypeFeature<TType>>();

if (!EqualityComparer<TType>.Default.Equals(typeFeature.Current, type))
{
typeFeature.ChangeDocumentType(type);
}
});
=> packageFactory.WithTemplate(templateFactory => templateFactory.Open(path, PackageOpenMode.Read), type);

internal static IPackageBuilder<TPackage> CreateTemplateBuilder<TPackage>(this IPackageBuilder<TPackage> builder, Func<IPackageFactory<TPackage>, TPackage> templateFactory, Action<TPackage>? onLoad = null)
public static IPackageFactory<TPackage> WithTemplate<TPackage, TType>(this IPackageFactory<TPackage> factory, Func<IPackageFactory<TPackage>, TPackage> templateActivator, TType type)
where TPackage : OpenXmlPackage
=> new TemplateBuilder<TPackage>(builder, templateFactory, onLoad);

private sealed class TemplateBuilder<TPackage> : IPackageBuilder<TPackage>
where TPackage : OpenXmlPackage
where TType : struct
{
private readonly IPackageBuilder<TPackage> _otherBuilder;
private readonly IPackageBuilder<TPackage> _templateBuilder;
private readonly Func<IPackageFactory<TPackage>, TPackage> _templateFactory;
private readonly Action<TPackage>? _onLoad;

public TemplateBuilder(
IPackageBuilder<TPackage> other,
Func<IPackageFactory<TPackage>, TPackage> templateFactory,
Action<TPackage>? onLoad)
{
_otherBuilder = other;
_templateBuilder = other.Clone();
_templateFactory = templateFactory;
_onLoad = onLoad;
}

public IDictionary<string, object?> Properties => _otherBuilder.Properties;

public IPackageBuilder<TPackage> Clone() => new TemplateBuilder<TPackage>(_otherBuilder.Clone(), _templateFactory, _onLoad);

public IPackageBuilder<TPackage> Use(Func<PackageInitializerDelegate<TPackage>, PackageInitializerDelegate<TPackage>> configure)
{
_otherBuilder.Use(configure);
return this;
}

public IPackageFactory<TPackage> Build() => new TemplateFactory(_otherBuilder.Build(), this);
var templateFactory = factory.New();

private sealed class TemplateFactory : IPackageFactory<TPackage>
factory.Template += package =>
{
private readonly IPackageFactory<TPackage> _factory;
private readonly TemplateBuilder<TPackage> _template;

public TemplateFactory(IPackageFactory<TPackage> factory, TemplateBuilder<TPackage> template)
{
_factory = factory;
_template = template;
}
var template = templateActivator(templateFactory);
template.Clone(package);

public TPackage Create(IPackageInitializer initializer)
=> _factory.Create(new Initializer(initializer, _template));
var typeFeature = package.Features.GetRequired<IDocumentTypeFeature<TType>>();

private sealed class Initializer : IPackageInitializer
if (!EqualityComparer<TType>.Default.Equals(typeFeature.Current, type))
{
private readonly IPackageInitializer _other;
private readonly TemplateBuilder<TPackage> _template;

public Initializer(IPackageInitializer other, TemplateBuilder<TPackage> template)
{
_other = other;
_template = template;
}

public void Initialize(OpenXmlPackage package)
{
_other.Initialize(package);
_template.LoadTemplate((TPackage)package);
}
typeFeature.ChangeDocumentType(type);
}
}

private void LoadTemplate(TPackage package)
{
using var template = _templateFactory(_templateBuilder.Build());

template.Clone(package);
};

_onLoad?.Invoke(package);
}
return factory;
}
}
Loading