55using DocumentFormat . OpenXml . Generator . Models ;
66using DocumentFormat . OpenXml . Generator . Schematron ;
77using System . CodeDom . Compiler ;
8+ using System . Runtime . CompilerServices ;
89using System . Text ;
10+ using System . Xml . Linq ;
911
1012namespace DocumentFormat . OpenXml . Generator . Generators . Elements ;
1113
1214public static class DataModelWriterExtensions
1315{
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+
14122 public static bool GetDataModelSyntax ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaNamespace model )
15123 {
16124 foreach ( var ns in GetNamespaces ( model , services ) . Distinct ( ) . OrderBy ( n => n ) )
@@ -63,9 +171,39 @@ private static string GetBaseName(SchemaType type)
63171 return type . BaseClass ;
64172 }
65173
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+
66190 private static void WriteType ( this IndentedTextWriter writer , OpenXmlGeneratorServices services , SchemaType element )
67191 {
68192 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+
69207 writer . Write ( "public " ) ;
70208
71209 if ( element . IsAbstract )
@@ -85,6 +223,10 @@ private static void WriteType(this IndentedTextWriter writer, OpenXmlGeneratorSe
85223 var delimiter = writer . TrackDelimiter ( separator : string . Empty , newLineCount : 2 ) ;
86224
87225 delimiter . AddDelimiter ( ) ;
226+
227+ writer . WriteTypeDetails ( services , element ) ;
228+ writer . WriteLineNoTabs ( ) ;
229+
88230 writer . WriteDocumentationComment ( $ "Initializes a new instance of the { className } class.") ;
89231 writer . Write ( element . GetAccessibility ( ) ) ;
90232 writer . Write ( " " ) ;
@@ -100,7 +242,16 @@ private static void WriteType(this IndentedTextWriter writer, OpenXmlGeneratorSe
100242 foreach ( var attribute in element . Attributes )
101243 {
102244 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+ }
104255 }
105256
106257 delimiter . AddDelimiter ( ) ;
@@ -110,7 +261,15 @@ private static void WriteType(this IndentedTextWriter writer, OpenXmlGeneratorSe
110261 {
111262 foreach ( var node in element . Children )
112263 {
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+ }
114273 }
115274 }
116275
@@ -187,9 +346,7 @@ private static void WriteMetadata(this IndentedTextWriter writer, OpenXmlGenerat
187346
188347 if ( ! containingType . Name . QName . IsEmpty )
189348 {
190- writer . Write ( "builder.SetSchema(" ) ;
191- writer . WriteItem ( containingType . Name . QName ) ;
192- writer . WriteLine ( ");" ) ;
349+ writer . WriteLine ( "builder.SetSchema(ElementType);" ) ;
193350 }
194351
195352 if ( ! containingType . IsAbstract && containingType . Version > OfficeVersion . Office2007 )
@@ -203,9 +360,13 @@ private static void WriteMetadata(this IndentedTextWriter writer, OpenXmlGenerat
203360 {
204361 foreach ( var child in containingType . KnownChildren )
205362 {
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 ( "());" ) ;
209370 }
210371 }
211372
@@ -298,7 +459,7 @@ void WriteUnion(IndentedTextWriter writer, string name, IEnumerable<Validator> v
298459 }
299460 }
300461
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 )
302463 {
303464 if ( string . IsNullOrEmpty ( element . PropertyName ) )
304465 {
@@ -320,22 +481,52 @@ private static void WriteElement(this IndentedTextWriter writer, OpenXmlGenerato
320481 Remarks = $ "xmlns:{ element . Name . QName . Prefix } = { services . GetNamespaceInfo ( element . Name . QName . Prefix ) . Uri } ",
321482 } ) ;
322483
484+ if ( attributeStrings is not null )
485+ {
486+ foreach ( string attributeString in attributeStrings )
487+ {
488+ writer . WriteLine ( attributeString ) ;
489+ }
490+ }
491+
323492 writer . Write ( "public " ) ;
324493 writer . Write ( className ) ;
325494 writer . Write ( "? " ) ;
326495 writer . WriteLine ( element . PropertyName ) ;
327496
328497 using ( writer . AddBlock ( new ( ) { IncludeTrailingNewline = false } ) )
329498 {
330- writer . Write ( "get => GetElement< " ) ;
499+ writer . Write ( "get => GetElement( " ) ;
331500 writer . Write ( className ) ;
332- writer . WriteLine ( ">();" ) ;
501+ writer . Write ( ".ElementType) as " ) ;
502+ writer . Write ( className ) ;
503+ writer . WriteLine ( ";" ) ;
333504
334- writer . WriteLine ( "set => SetElement(value);" ) ;
505+ writer . Write ( "set => SetElement(value, " ) ;
506+ writer . Write ( className ) ;
507+ writer . WriteLine ( ".ElementType);" ) ;
335508 }
336509 }
337510
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 )
339530 {
340531 var remarks = default ( string ) ;
341532 var info = services . GetNamespaceInfo ( attribute . QName . Prefix ) ;
@@ -359,6 +550,14 @@ private static void WriteAttributeProperty(this IndentedTextWriter writer, OpenX
359550 Remarks = remarks ,
360551 } ) ;
361552
553+ if ( attributeStrings is not null )
554+ {
555+ foreach ( string attributeString in attributeStrings )
556+ {
557+ writer . WriteLine ( attributeString ) ;
558+ }
559+ }
560+
362561 writer . Write ( "public " ) ;
363562 writer . Write ( attribute . Type ) ;
364563 writer . Write ( "? " ) ;
0 commit comments