Skip to content

Commit 585bd6a

Browse files
authored
Enable package initialization that doesn't get copied (#1825)
With the pipelining added in 3.0.1 used internally for creating packages, it caused any initializing of the package (i.e. from FlatOpc) to be repeated on clones which we don't want. This fixes the templates (both FlatOpc and document templates) from not being captures in the package creation but kept separate for an initialization step. Fixes #1814
1 parent 55eb310 commit 585bd6a

File tree

16 files changed

+1685
-260
lines changed

16 files changed

+1685
-260
lines changed

src/DocumentFormat.OpenXml.Framework/Builder/IPackageBuilder{TPackage}.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace DocumentFormat.OpenXml.Builder;
1111
/// <summary>
1212
/// A delegate for initializing a package.
1313
/// </summary>
14-
internal delegate void PackageInitializerDelegate<TPackage>(TPackage package);
14+
internal delegate void PackageDelegate<TPackage>(TPackage package);
1515

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

3737
/// <summary>
3838
/// Create a copy of the builder that will be independent of the original, but retains the existing middleware and properties.

src/DocumentFormat.OpenXml.Framework/Builder/IPackageFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@ internal interface IPackageFactory<TPackage>
2222
/// <param name="initializer">Initializer for the package.</param>
2323
/// <returns>The created package.</returns>
2424
TPackage Create(IPackageInitializer initializer);
25+
26+
PackageDelegate<TPackage>? Template { get; set; }
27+
28+
IPackageFactory<TPackage> New();
2529
}

src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilder.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ internal abstract class OpenXmlPackageBuilder<TPackage> : IPackageBuilder<TPacka
1313
where TPackage : OpenXmlPackage
1414
{
1515
private Dictionary<string, object?>? _properties;
16-
private PackageInitializerDelegate<TPackage>? _pipeline;
16+
private PackageDelegate<TPackage>? _pipeline;
1717
private bool _isLocked;
18-
private List<Func<PackageInitializerDelegate<TPackage>, PackageInitializerDelegate<TPackage>>>? _middleware;
18+
private List<Func<PackageDelegate<TPackage>, PackageDelegate<TPackage>>>? _middleware;
1919

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

@@ -37,7 +37,7 @@ internal OpenXmlPackageBuilder(OpenXmlPackageBuilder<TPackage>? parent)
3737
}
3838
}
3939

40-
public IPackageBuilder<TPackage> Use(Func<PackageInitializerDelegate<TPackage>, PackageInitializerDelegate<TPackage>> configure)
40+
public IPackageBuilder<TPackage> Use(Func<PackageDelegate<TPackage>, PackageDelegate<TPackage>> configure)
4141
{
4242
if (_isLocked)
4343
{
@@ -58,7 +58,7 @@ public IPackageBuilder<TPackage> Use(Func<PackageInitializerDelegate<TPackage>,
5858
public IPackageFactory<TPackage> Build() => new Factory(Create, BuildPipeline());
5959

6060
[MemberNotNull(nameof(_pipeline))]
61-
private PackageInitializerDelegate<TPackage> BuildPipeline()
61+
private PackageDelegate<TPackage> BuildPipeline()
6262
{
6363
_isLocked = true;
6464

@@ -68,11 +68,11 @@ private PackageInitializerDelegate<TPackage> BuildPipeline()
6868
}
6969

7070
var factory = new PackageFactoryFeature(Clone());
71-
var pipeline = new PackageInitializerDelegate<TPackage>(factory.PipelineTerminator);
71+
var pipeline = new PackageDelegate<TPackage>(factory.PipelineTerminator);
7272

7373
if (_middleware is not null)
7474
{
75-
for (int i = _middleware.Count - 1; i >= 0; i--)
75+
for (var i = _middleware.Count - 1; i >= 0; i--)
7676
{
7777
pipeline = _middleware[i](pipeline);
7878
}
@@ -84,20 +84,27 @@ private PackageInitializerDelegate<TPackage> BuildPipeline()
8484
private sealed class Factory : IPackageFactory<TPackage>
8585
{
8686
private readonly Func<TPackage> _package;
87-
private readonly PackageInitializerDelegate<TPackage> _pipeline;
87+
private readonly PackageDelegate<TPackage> _pipeline;
8888

89-
public Factory(Func<TPackage> package, PackageInitializerDelegate<TPackage> pipeline)
89+
public Factory(Func<TPackage> package, PackageDelegate<TPackage> pipeline)
9090
{
9191
_package = package;
9292
_pipeline = pipeline;
9393
}
9494

95+
public PackageDelegate<TPackage>? Template { get; set; }
96+
97+
public IPackageFactory<TPackage> New() => new Factory(_package, _pipeline);
98+
9599
public TPackage Create(IPackageInitializer initializer)
96100
{
97101
var package = _package();
98102

99103
initializer.Initialize(package);
104+
105+
package.Features.Set<TemplateFeature>(new TemplateFeature(Template));
100106
_pipeline(package);
107+
package.Features.Set<TemplateFeature>(null);
101108

102109
return package;
103110
}
@@ -114,6 +121,23 @@ private sealed class PackageFactoryFeature : IPackageFactoryFeature<TPackage>
114121
public void PipelineTerminator(TPackage package)
115122
{
116123
package.Features.Set<IPackageFactoryFeature<TPackage>>(this);
124+
125+
if (package.Features.Get<TemplateFeature>() is { } feature)
126+
{
127+
feature.Initialize(package);
128+
}
117129
}
118130
}
131+
132+
private sealed class TemplateFeature
133+
{
134+
private readonly PackageDelegate<TPackage>? _initializer;
135+
136+
public TemplateFeature(PackageDelegate<TPackage>? initializer)
137+
{
138+
_initializer = initializer;
139+
}
140+
141+
public void Initialize(TPackage package) => _initializer?.Invoke(package);
142+
}
119143
}

src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ internal static TPackage Open<TPackage>(this IPackageFactory<TPackage> builder)
7474
where TPackage : OpenXmlPackage
7575
=> builder.Create(new StreamPackageFeature(new MemoryStream(), PackageOpenMode.Create));
7676

77-
internal static TPackage Use<TPackage>(this TPackage package, PackageInitializerDelegate<TPackage> action)
77+
internal static TPackage Use<TPackage>(this TPackage package, PackageDelegate<TPackage> action)
7878
where TPackage : OpenXmlPackage
7979
{
8080
action(package);
8181
return package;
8282
}
8383

84-
internal static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPackage> builder, PackageInitializerDelegate<TPackage> action)
84+
internal static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPackage> builder, PackageDelegate<TPackage> action)
8585
where TPackage : OpenXmlPackage
8686
=> builder.Use((package, next) =>
8787
{
@@ -92,7 +92,7 @@ internal static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPa
9292
/// <summary>
9393
/// Adds the <paramref name="middleware"/> to the builder for initializing a package.
9494
/// </summary>
95-
public static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPackage> builder, Action<TPackage, PackageInitializerDelegate<TPackage>> middleware)
95+
public static IPackageBuilder<TPackage> Use<TPackage>(this IPackageBuilder<TPackage> builder, Action<TPackage, PackageDelegate<TPackage>> middleware)
9696
where TPackage : OpenXmlPackage
9797
{
9898
if (builder is null)

src/DocumentFormat.OpenXml.Framework/Builder/TemplateBuilderExtensions.cs

Lines changed: 13 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -16,99 +16,30 @@ namespace DocumentFormat.OpenXml.Builder;
1616
[Obsolete(ExperimentalApis.Message, DiagnosticId = ExperimentalApis.PackageBuilder, UrlFormat = ExperimentalApis.UrlFormat)]
1717
internal static class TemplateBuilderExtensions
1818
{
19-
/// <summary>
20-
/// Adds a template to the current <paramref name="builder"/>.
21-
/// </summary>
22-
public static IPackageBuilder<TPackage> UseTemplate<TPackage, TType>(this IPackageBuilder<TPackage> builder, string path, TType type)
19+
public static IPackageFactory<TPackage> WithTemplate<TPackage, TType>(this IPackageFactory<TPackage> packageFactory, string path, TType type)
2320
where TPackage : OpenXmlPackage
2421
where TType : struct
25-
=> builder.CreateTemplateBuilder(
26-
builder => builder.Open(path, PackageOpenMode.Read),
27-
package =>
28-
{
29-
var typeFeature = package.Features.GetRequired<IDocumentTypeFeature<TType>>();
30-
31-
if (!EqualityComparer<TType>.Default.Equals(typeFeature.Current, type))
32-
{
33-
typeFeature.ChangeDocumentType(type);
34-
}
35-
});
22+
=> packageFactory.WithTemplate(templateFactory => templateFactory.Open(path, PackageOpenMode.Read), type);
3623

37-
internal static IPackageBuilder<TPackage> CreateTemplateBuilder<TPackage>(this IPackageBuilder<TPackage> builder, Func<IPackageFactory<TPackage>, TPackage> templateFactory, Action<TPackage>? onLoad = null)
24+
public static IPackageFactory<TPackage> WithTemplate<TPackage, TType>(this IPackageFactory<TPackage> factory, Func<IPackageFactory<TPackage>, TPackage> templateActivator, TType type)
3825
where TPackage : OpenXmlPackage
39-
=> new TemplateBuilder<TPackage>(builder, templateFactory, onLoad);
40-
41-
private sealed class TemplateBuilder<TPackage> : IPackageBuilder<TPackage>
42-
where TPackage : OpenXmlPackage
26+
where TType : struct
4327
{
44-
private readonly IPackageBuilder<TPackage> _otherBuilder;
45-
private readonly IPackageBuilder<TPackage> _templateBuilder;
46-
private readonly Func<IPackageFactory<TPackage>, TPackage> _templateFactory;
47-
private readonly Action<TPackage>? _onLoad;
48-
49-
public TemplateBuilder(
50-
IPackageBuilder<TPackage> other,
51-
Func<IPackageFactory<TPackage>, TPackage> templateFactory,
52-
Action<TPackage>? onLoad)
53-
{
54-
_otherBuilder = other;
55-
_templateBuilder = other.Clone();
56-
_templateFactory = templateFactory;
57-
_onLoad = onLoad;
58-
}
59-
60-
public IDictionary<string, object?> Properties => _otherBuilder.Properties;
61-
62-
public IPackageBuilder<TPackage> Clone() => new TemplateBuilder<TPackage>(_otherBuilder.Clone(), _templateFactory, _onLoad);
63-
64-
public IPackageBuilder<TPackage> Use(Func<PackageInitializerDelegate<TPackage>, PackageInitializerDelegate<TPackage>> configure)
65-
{
66-
_otherBuilder.Use(configure);
67-
return this;
68-
}
69-
70-
public IPackageFactory<TPackage> Build() => new TemplateFactory(_otherBuilder.Build(), this);
28+
var templateFactory = factory.New();
7129

72-
private sealed class TemplateFactory : IPackageFactory<TPackage>
30+
factory.Template += package =>
7331
{
74-
private readonly IPackageFactory<TPackage> _factory;
75-
private readonly TemplateBuilder<TPackage> _template;
76-
77-
public TemplateFactory(IPackageFactory<TPackage> factory, TemplateBuilder<TPackage> template)
78-
{
79-
_factory = factory;
80-
_template = template;
81-
}
32+
var template = templateActivator(templateFactory);
33+
template.Clone(package);
8234

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

86-
private sealed class Initializer : IPackageInitializer
37+
if (!EqualityComparer<TType>.Default.Equals(typeFeature.Current, type))
8738
{
88-
private readonly IPackageInitializer _other;
89-
private readonly TemplateBuilder<TPackage> _template;
90-
91-
public Initializer(IPackageInitializer other, TemplateBuilder<TPackage> template)
92-
{
93-
_other = other;
94-
_template = template;
95-
}
96-
97-
public void Initialize(OpenXmlPackage package)
98-
{
99-
_other.Initialize(package);
100-
_template.LoadTemplate((TPackage)package);
101-
}
39+
typeFeature.ChangeDocumentType(type);
10240
}
103-
}
104-
105-
private void LoadTemplate(TPackage package)
106-
{
107-
using var template = _templateFactory(_templateBuilder.Build());
108-
109-
template.Clone(package);
41+
};
11042

111-
_onLoad?.Invoke(package);
112-
}
43+
return factory;
11344
}
11445
}

0 commit comments

Comments
 (0)