Skip to content

Commit 1238452

Browse files
authored
Add namespace generator to SDK (#1092)
* Add namespace generator to SDK * "" should be office 2007 * fix reflection using
1 parent 66dab96 commit 1238452

24 files changed

+1337
-419
lines changed

gen/DocumentFormat.OpenXml.Generator/Data/namespaces.json

Lines changed: 660 additions & 0 deletions
Large diffs are not rendered by default.

gen/DocumentFormat.OpenXml.Generator/DocumentFormat.OpenXml.Generator.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
<IncludeInitShim>true</IncludeInitShim>
88
</PropertyGroup>
99

10+
<ItemGroup>
11+
<EmbeddedResource Include="Data\*.json" />
12+
</ItemGroup>
13+
1014
<ItemGroup>
1115
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
1216
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
namespace DocumentFormat.OpenXml.Generator.Models;
5+
6+
public class NamespaceInfo
7+
{
8+
public string Prefix { get; set; } = null!;
9+
10+
public string Uri { get; set; } = null!;
11+
12+
public OfficeVersion Version { get; set; }
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
namespace DocumentFormat.OpenXml.Generator.Models;
5+
6+
public enum OfficeVersion
7+
{
8+
Office2007 = 0,
9+
Office2010 = 1,
10+
Office2013 = 2,
11+
Office2016 = 3,
12+
Office2019 = 4,
13+
Office2021 = 5,
14+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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 Microsoft.CodeAnalysis;
5+
using System;
6+
7+
namespace DocumentFormat.OpenXml.Generator.NamespaceGeneration;
8+
9+
[Generator]
10+
internal class NamespaceGenerator : ISourceGenerator
11+
{
12+
public void Execute(GeneratorExecutionContext context)
13+
{
14+
if (string.Equals(context.Compilation.AssemblyName, "DocumentFormat.OpenXml", StringComparison.Ordinal))
15+
{
16+
context.AddSource("Namespaces", OpenXmlGeneratorContext.Shared.Namespaces.Generate());
17+
}
18+
}
19+
20+
public void Initialize(GeneratorInitializationContext context)
21+
{
22+
}
23+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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 DocumentFormat.OpenXml.Generator.Models;
5+
using System.CodeDom.Compiler;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
10+
namespace DocumentFormat.OpenXml.Generator.NamespaceGeneration;
11+
12+
internal static class NamespaceGeneratorExtensions
13+
{
14+
public static string Generate(this IEnumerable<NamespaceInfo> namespaces)
15+
{
16+
var sb = new StringWriter();
17+
var writer = new IndentedTextWriter(sb);
18+
19+
writer.WriteFileHeader();
20+
writer.WriteLine("using System.Collections.Generic;");
21+
writer.WriteLine();
22+
writer.WriteLine("namespace DocumentFormat.OpenXml.Features;");
23+
writer.WriteLine();
24+
writer.WriteLine("internal partial class OpenXmlNamespaceResolver");
25+
26+
using (writer.AddBlock())
27+
{
28+
writer.WriteLine("private readonly Dictionary<string, string> _urlToPrefix = new()");
29+
30+
using (writer.AddBlock(includeSemiColon: true))
31+
{
32+
foreach (var ns in namespaces)
33+
{
34+
writer.WriteList(ns.Uri, ns.Prefix);
35+
writer.WriteLine(",");
36+
}
37+
}
38+
39+
writer.WriteLineNoTabs();
40+
writer.WriteLine("private readonly Dictionary<string, string> _prefixToUrl = new()");
41+
42+
using (writer.AddBlock(includeSemiColon: true))
43+
{
44+
foreach (var ns in namespaces)
45+
{
46+
writer.WriteList(ns.Prefix, ns.Uri);
47+
writer.WriteLine(",");
48+
}
49+
}
50+
51+
writer.WriteLineNoTabs();
52+
writer.WriteLine("private readonly Dictionary<string, FileFormatVersions> _prefixToVersion = new()");
53+
54+
using (writer.AddBlock(includeSemiColon: true))
55+
{
56+
foreach (var ns in namespaces)
57+
{
58+
writer.WriteList(ns.Prefix, ns.Version);
59+
60+
writer.WriteLine(",");
61+
}
62+
}
63+
}
64+
65+
return sb.ToString();
66+
}
67+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 DocumentFormat.OpenXml.Generator.Models;
5+
using Newtonsoft.Json;
6+
using Newtonsoft.Json.Converters;
7+
using System;
8+
using System.Collections.Generic;
9+
using System.IO;
10+
using System.Linq;
11+
12+
namespace DocumentFormat.OpenXml.Generator;
13+
14+
public record OpenXmlGeneratorContext
15+
{
16+
private static readonly JsonSerializer _serializer = JsonSerializer.Create(new()
17+
{
18+
Converters = new[]
19+
{
20+
new StringEnumConverter(),
21+
},
22+
});
23+
24+
public static OpenXmlGeneratorContext Shared { get; } = Load();
25+
26+
public IEnumerable<NamespaceInfo> Namespaces { get; init; } = Enumerable.Empty<NamespaceInfo>();
27+
28+
public static OpenXmlGeneratorContext Load()
29+
=> new()
30+
{
31+
Namespaces = Load<NamespaceInfo[]>("namespaces.json"),
32+
};
33+
34+
private static T Load<T>(string name)
35+
{
36+
using var stream = typeof(OpenXmlGeneratorContext).Assembly.GetManifestResourceStream(typeof(OpenXmlGeneratorContext), $"Data.{name}");
37+
38+
if (stream is null)
39+
{
40+
throw new InvalidOperationException($"Could not find stream '{name}'");
41+
}
42+
43+
using var text = new StreamReader(stream);
44+
using var reader = new JsonTextReader(text);
45+
46+
return _serializer.Deserialize<T>(reader)!;
47+
}
48+
}

gen/DocumentFormat.OpenXml.Generator/TextWriterExtensions.cs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using DocumentFormat.OpenXml.Generator.Models;
45
using System;
56
using System.CodeDom.Compiler;
67
using System.IO;
@@ -42,11 +43,59 @@ public static void WriteEnum<T>(this TextWriter writer, string typeName, T value
4243
writer.Write(value.ToString());
4344
}
4445

46+
public static void WriteList<T1, T2>(this TextWriter writer, T1 item1, T2 item2)
47+
{
48+
writer.Write("{ ");
49+
writer.WriteItem(item1);
50+
writer.Write(", ");
51+
writer.WriteItem(item2);
52+
writer.Write(" ");
53+
writer.Write("}");
54+
}
55+
56+
public static void WriteList<T1, T2, T3>(this TextWriter writer, T1 item1, T2 item2, T3 item3)
57+
{
58+
writer.Write("{ ");
59+
writer.WriteItem(item1);
60+
writer.Write(", ");
61+
writer.WriteItem(item2);
62+
writer.Write(", ");
63+
writer.WriteItem(item3);
64+
writer.Write(" ");
65+
writer.Write("}");
66+
}
67+
68+
public static void WriteItem<T>(this TextWriter writer, T item)
69+
{
70+
if (item is null)
71+
{
72+
writer.WriteNull();
73+
}
74+
else if (typeof(T) == typeof(Verbatim))
75+
{
76+
writer.Write(((Verbatim)(object)item).Value);
77+
}
78+
else if (typeof(T) == typeof(OfficeVersion))
79+
{
80+
writer.WriteEnum("FileFormatVersions", (OfficeVersion)(object)item);
81+
}
82+
else if (typeof(T) == typeof(string))
83+
{
84+
writer.WriteString((string)(object)item);
85+
}
86+
else
87+
{
88+
writer.Write(item.ToString());
89+
}
90+
}
91+
92+
public static void WriteNull(this TextWriter writer) => writer.Write("null");
93+
4594
public static void WriteString(this TextWriter writer, string input)
4695
{
4796
if (input is null)
4897
{
49-
writer.Write("null");
98+
writer.WriteNull();
5099
}
51100
else if (input.Length == 0)
52101
{
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
namespace DocumentFormat.OpenXml.Generator;
5+
6+
internal readonly struct Verbatim
7+
{
8+
public Verbatim(string value)
9+
{
10+
Value = value;
11+
}
12+
13+
public string Value { get; }
14+
}

src/DocumentFormat.OpenXml/Features/DefaultFeatures.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ internal partial class DefaultFeatures : IFeatureCollection
1717

1818
[KnownFeature(typeof(IRootElementFactory), typeof(ReflectionBasedRootElementFactory))]
1919
[KnownFeature(typeof(IPartMetadataFeature), typeof(CachedPartMetadataProvider))]
20+
[KnownFeature(typeof(IOpenXmlNamespaceResolver), typeof(OpenXmlNamespaceResolver))]
21+
[KnownFeature(typeof(IOpenXmlNamespaceIdResolver), typeof(OpenXmlNamespaceIdResolver))]
2022
public partial TFeature? Get<TFeature>();
2123

2224
public void Set<TFeature>(TFeature? instance)

0 commit comments

Comments
 (0)