5
5
using DocumentFormat . OpenXml . Generator . Models ;
6
6
using DocumentFormat . OpenXml . Generator . Schematron ;
7
7
using System . CodeDom . Compiler ;
8
+ using System . Runtime . CompilerServices ;
8
9
using System . Text ;
10
+ using System . Xml . Linq ;
9
11
10
12
namespace DocumentFormat . OpenXml . Generator . Generators . Elements ;
11
13
12
14
public static class DataModelWriterExtensions
13
15
{
16
+ public static class AttributeStrings
17
+ {
18
+ public const string ObsoletePropertyWarn = "[Obsolete(\" Unused property, will be removed in a future version.\" , false)]" ;
19
+ public const string ObsoletePropertyError = "[Obsolete(\" Unused property, will be removed in a future version.\" , true)]" ;
20
+ public const string ObsoleteAttributeWarn = "[Obsolete(\" Unused attribute, will be removed in a future version.\" , false)]" ;
21
+ public const string ObsoleteAttributeError = "[Obsolete(\" Unused attribute, will be removed in a future version.\" , true)]" ;
22
+ public const string EditorBrowsableAlways = "[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always)] " ;
23
+ public const string EditorBrowsableAdvanced = "[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] " ;
24
+ public const string EditorBrowsableNever = "[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] " ;
25
+ }
26
+
27
+ private static readonly List < string > ObsoletePropertyWarnList =
28
+ [
29
+ AttributeStrings . ObsoletePropertyWarn ,
30
+ AttributeStrings . EditorBrowsableNever ,
31
+ ] ;
32
+
33
+ // Use this dictionary to add attributes like ObsoleteAttribute or other directives to classes, child elements or attributes.
34
+ private static readonly Dictionary < TypedQName , Dictionary < TypedQName , List < string > > > _attributeData =
35
+ new Dictionary < TypedQName , Dictionary < TypedQName , List < string > > > ( )
36
+ {
37
+ // Example with annotations:
38
+ // {
39
+ // This is the containing complex type class, in the json metadata, this comes from the fully qualified "Name": "c:CT_BubbleSer/c15:ser",
40
+ // "c:CT_BubbleSer/c15:ser",
41
+ // new Dictionary<TypedQName, List<string>>()
42
+ // {
43
+ // {
44
+ // This is an example of obsoleting the whole class.
45
+ // In the json metadata:
46
+ // Use the same fully qualified name as the class, for example "Name": "c:CT_BubbleSer/c15:ser",
47
+ // "c:CT_BubbleSer/c15:ser",
48
+ // ObsoleteClassErrorList
49
+ // },
50
+ // {
51
+ // This is an example obsoleting a child element (property in C#)
52
+ // In the json metadata:
53
+ // For child elements, this comes from "Name": "c:CT_PictureOptions/c:pictureOptions",
54
+ // "c:CT_PictureOptions/c:pictureOptions",
55
+ // ObsoletePropertyWarnList
56
+ // },
57
+ // {
58
+ // This is an example obsoleting a child attribute (property in C#)
59
+ // In the json metadata: use for example "QName" converted to a TypedQName string using the C# type from the
60
+ // Type property with no prefix: ":StringValue/:formatCode",
61
+ // ":StringValue/:formatCode",
62
+ // ObsoleteAttributeWarnList
63
+ // },
64
+ // }
65
+ // },
66
+ {
67
+ "c:CT_BubbleSer/c15:ser" ,
68
+ new Dictionary < TypedQName , List < string > > ( )
69
+ {
70
+ {
71
+ "c:CT_PictureOptions/c:pictureOptions" ,
72
+ ObsoletePropertyWarnList
73
+ } ,
74
+ }
75
+ } ,
76
+ {
77
+ "c:CT_LineSer/c15:ser" ,
78
+ new Dictionary < TypedQName , List < string > > ( )
79
+ {
80
+ {
81
+ "c:CT_PictureOptions/c:pictureOptions" ,
82
+ ObsoletePropertyWarnList
83
+ } ,
84
+ }
85
+ } ,
86
+ {
87
+ "c:CT_PieSer/c15:ser" ,
88
+ new Dictionary < TypedQName , List < string > > ( )
89
+ {
90
+ {
91
+ "c:CT_PictureOptions/c:pictureOptions" ,
92
+ ObsoletePropertyWarnList
93
+ } ,
94
+ }
95
+ } ,
96
+ {
97
+ "c:CT_RadarSer/c15:ser" ,
98
+ new Dictionary < TypedQName , List < string > > ( )
99
+ {
100
+ {
101
+ "c:CT_PictureOptions/c:pictureOptions" ,
102
+ ObsoletePropertyWarnList
103
+ } ,
104
+ }
105
+ } ,
106
+ {
107
+ "c:CT_SurfaceSer/c15:ser" ,
108
+ new Dictionary < TypedQName , List < string > > ( )
109
+ {
110
+ {
111
+ "c:CT_PictureOptions/c:pictureOptions" ,
112
+ ObsoletePropertyWarnList
113
+ } ,
114
+ {
115
+ "c:CT_Boolean/c:bubble3D" ,
116
+ ObsoletePropertyWarnList
117
+ } ,
118
+ }
119
+ } ,
120
+ } ;
121
+
14
122
public static bool GetDataModelSyntax ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaNamespace model )
15
123
{
16
124
foreach ( var ns in GetNamespaces ( model , services ) . Distinct ( ) . OrderBy ( n => n ) )
@@ -63,9 +171,39 @@ private static string GetBaseName(SchemaType type)
63
171
return type . BaseClass ;
64
172
}
65
173
174
+ private static void WriteTypeDetails ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaType element )
175
+ {
176
+ // Since some types will not be shadowing an existing static type, it's easier to just disable the warning
177
+ writer . WriteLine ( "#pragma warning disable CS0109" ) ;
178
+ writer . Write ( "internal static readonly new OpenXmlQualifiedName ElementQName = " ) ;
179
+ writer . WriteQName ( services , element . Name . QName ) ;
180
+ writer . WriteLine ( ";" ) ;
181
+
182
+ writer . Write ( "internal static readonly new OpenXmlQualifiedName ElementTypeName = " ) ;
183
+ writer . WriteQName ( services , element . Name . Type ) ;
184
+ writer . WriteLine ( ";" ) ;
185
+
186
+ writer . WriteLine ( "internal static readonly new OpenXmlSchemaType ElementType = new(ElementQName, ElementTypeName);" ) ;
187
+ writer . WriteLine ( "#pragma warning restore CS0109" ) ;
188
+ }
189
+
66
190
private static void WriteType ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaType element )
67
191
{
68
192
writer . WriteDocumentationComment ( BuildTypeComments ( services , element ) ) ;
193
+
194
+ if ( _attributeData . TryGetValue ( element . Name , out Dictionary < TypedQName , List < string > > ctAttributeData ) )
195
+ {
196
+ // if the fully qualified CT/tag name is also one of the children of the dictionary that means the attributes of that
197
+ // child's list need to be applied to the whole class, for example, if we're obsoleting an entire class.
198
+ if ( ctAttributeData . TryGetValue ( element . Name , out List < string > attributeStrings ) )
199
+ {
200
+ foreach ( string attributeString in attributeStrings )
201
+ {
202
+ writer . WriteLine ( attributeString ) ;
203
+ }
204
+ }
205
+ }
206
+
69
207
writer . Write ( "public " ) ;
70
208
71
209
if ( element . IsAbstract )
@@ -85,6 +223,10 @@ private static void WriteType(this IndentedTextWriter writer, OpenXmlGeneratorSe
85
223
var delimiter = writer . TrackDelimiter ( separator : string . Empty , newLineCount : 2 ) ;
86
224
87
225
delimiter . AddDelimiter ( ) ;
226
+
227
+ writer . WriteTypeDetails ( services , element ) ;
228
+ writer . WriteLineNoTabs ( ) ;
229
+
88
230
writer . WriteDocumentationComment ( $ "Initializes a new instance of the { className } class.") ;
89
231
writer . Write ( element . GetAccessibility ( ) ) ;
90
232
writer . Write ( " " ) ;
@@ -100,7 +242,16 @@ private static void WriteType(this IndentedTextWriter writer, OpenXmlGeneratorSe
100
242
foreach ( var attribute in element . Attributes )
101
243
{
102
244
delimiter . AddDelimiter ( ) ;
103
- writer . WriteAttributeProperty ( services , attribute ) ;
245
+
246
+ if ( _attributeData . TryGetValue ( element . Name , out Dictionary < TypedQName , List < string > > attrAttributeData )
247
+ && attrAttributeData . TryGetValue ( ":" + attribute . Type + "/" + attribute . QName . ToString ( ) , out List < string > attrAttributeStrings ) )
248
+ {
249
+ writer . WriteAttributeProperty ( services , attribute , attrAttributeStrings ) ;
250
+ }
251
+ else
252
+ {
253
+ writer . WriteAttributeProperty ( services , attribute ) ;
254
+ }
104
255
}
105
256
106
257
delimiter . AddDelimiter ( ) ;
@@ -110,7 +261,15 @@ private static void WriteType(this IndentedTextWriter writer, OpenXmlGeneratorSe
110
261
{
111
262
foreach ( var node in element . Children )
112
263
{
113
- writer . WriteElement ( services , element , node , ref delimiter ) ;
264
+ if ( _attributeData . TryGetValue ( element . Name , out Dictionary < TypedQName , List < string > > childAttributeData )
265
+ && childAttributeData . TryGetValue ( node . Name , out List < string > childAttributeStrings ) )
266
+ {
267
+ writer . WriteElement ( services , element , node , ref delimiter , childAttributeStrings ) ;
268
+ }
269
+ else
270
+ {
271
+ writer . WriteElement ( services , element , node , ref delimiter ) ;
272
+ }
114
273
}
115
274
}
116
275
@@ -187,9 +346,7 @@ private static void WriteMetadata(this IndentedTextWriter writer, OpenXmlGenerat
187
346
188
347
if ( ! containingType . Name . QName . IsEmpty )
189
348
{
190
- writer . Write ( "builder.SetSchema(" ) ;
191
- writer . WriteItem ( containingType . Name . QName ) ;
192
- writer . WriteLine ( ");" ) ;
349
+ writer . WriteLine ( "builder.SetSchema(ElementType);" ) ;
193
350
}
194
351
195
352
if ( ! containingType . IsAbstract && containingType . Version > OfficeVersion . Office2007 )
@@ -203,9 +360,13 @@ private static void WriteMetadata(this IndentedTextWriter writer, OpenXmlGenerat
203
360
{
204
361
foreach ( var child in containingType . KnownChildren )
205
362
{
206
- writer . Write ( "builder.AddChild<" ) ;
207
- writer . Write ( services . FindClassName ( child ) ) ;
208
- writer . WriteLine ( ">();" ) ;
363
+ var className = services . FindClassName ( child ) ;
364
+
365
+ writer . Write ( "builder.AddChild(" ) ;
366
+ writer . Write ( className ) ;
367
+ writer . Write ( ".ElementType, static () => new " ) ;
368
+ writer . Write ( className ) ;
369
+ writer . WriteLine ( "());" ) ;
209
370
}
210
371
}
211
372
@@ -298,7 +459,7 @@ void WriteUnion(IndentedTextWriter writer, string name, IEnumerable<Validator> v
298
459
}
299
460
}
300
461
301
- private static void WriteElement ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaType parent , SchemaElement element , ref Delimiter delimiter )
462
+ private static void WriteElement ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaType parent , SchemaElement element , ref Delimiter delimiter , List < string > ? attributeStrings = null )
302
463
{
303
464
if ( string . IsNullOrEmpty ( element . PropertyName ) )
304
465
{
@@ -320,22 +481,52 @@ private static void WriteElement(this IndentedTextWriter writer, OpenXmlGenerato
320
481
Remarks = $ "xmlns:{ element . Name . QName . Prefix } = { services . GetNamespaceInfo ( element . Name . QName . Prefix ) . Uri } ",
321
482
} ) ;
322
483
484
+ if ( attributeStrings is not null )
485
+ {
486
+ foreach ( string attributeString in attributeStrings )
487
+ {
488
+ writer . WriteLine ( attributeString ) ;
489
+ }
490
+ }
491
+
323
492
writer . Write ( "public " ) ;
324
493
writer . Write ( className ) ;
325
494
writer . Write ( "? " ) ;
326
495
writer . WriteLine ( element . PropertyName ) ;
327
496
328
497
using ( writer . AddBlock ( new ( ) { IncludeTrailingNewline = false } ) )
329
498
{
330
- writer . Write ( "get => GetElement< " ) ;
499
+ writer . Write ( "get => GetElement( " ) ;
331
500
writer . Write ( className ) ;
332
- writer . WriteLine ( ">();" ) ;
501
+ writer . Write ( ".ElementType) as " ) ;
502
+ writer . Write ( className ) ;
503
+ writer . WriteLine ( ";" ) ;
333
504
334
- writer . WriteLine ( "set => SetElement(value);" ) ;
505
+ writer . Write ( "set => SetElement(value, " ) ;
506
+ writer . Write ( className ) ;
507
+ writer . WriteLine ( ".ElementType);" ) ;
335
508
}
336
509
}
337
510
338
- private static void WriteAttributeProperty ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaAttribute attribute )
511
+ private static void WriteQName ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , QName qname )
512
+ {
513
+ writer . Write ( "new(" ) ;
514
+ writer . WriteString ( services . GetNamespaceInfo ( qname . Prefix ) . Uri ) ;
515
+ writer . Write ( ", " ) ;
516
+ writer . WriteString ( qname . Name ) ;
517
+ writer . Write ( ")" ) ;
518
+ }
519
+
520
+ internal static void WriteTypedName ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , TypedQName typed )
521
+ {
522
+ writer . Write ( "new(" ) ;
523
+ writer . WriteQName ( services , typed . Type ) ;
524
+ writer . Write ( ", " ) ;
525
+ writer . WriteQName ( services , typed . QName ) ;
526
+ writer . Write ( ")" ) ;
527
+ }
528
+
529
+ private static void WriteAttributeProperty ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaAttribute attribute , List < string > ? attributeStrings = null )
339
530
{
340
531
var remarks = default ( string ) ;
341
532
var info = services . GetNamespaceInfo ( attribute . QName . Prefix ) ;
@@ -359,6 +550,14 @@ private static void WriteAttributeProperty(this IndentedTextWriter writer, OpenX
359
550
Remarks = remarks ,
360
551
} ) ;
361
552
553
+ if ( attributeStrings is not null )
554
+ {
555
+ foreach ( string attributeString in attributeStrings )
556
+ {
557
+ writer . WriteLine ( attributeString ) ;
558
+ }
559
+ }
560
+
362
561
writer . Write ( "public " ) ;
363
562
writer . Write ( attribute . Type ) ;
364
563
writer . Write ( "? " ) ;
0 commit comments