aboutsummaryrefslogtreecommitdiff
path: root/src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-05-11 07:37:18 -0700
committerRob Mensching <rob@firegiant.com>2021-05-11 07:37:18 -0700
commit985b2683b2717c83a27295b36e09b126083238f9 (patch)
tree622804d279e0ac58366dee62888e6a317a2b9d41 /src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs
parent3f583916719eeef598d10a5d4e14ef14f008243b (diff)
parent6a24996a2e831cfe402398af65b31fb1ecd575a9 (diff)
downloadwix-985b2683b2717c83a27295b36e09b126083238f9.tar.gz
wix-985b2683b2717c83a27295b36e09b126083238f9.tar.bz2
wix-985b2683b2717c83a27295b36e09b126083238f9.zip
Merge WixBuildTools
Diffstat (limited to 'src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs')
-rw-r--r--src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs b/src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs
new file mode 100644
index 00000000..6f51dbf9
--- /dev/null
+++ b/src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs
@@ -0,0 +1,250 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.MsgGen
4{
5 using System;
6 using System.CodeDom;
7 using System.Collections;
8 using System.Globalization;
9 using System.Reflection;
10 using System.Resources;
11 using System.Xml;
12
13 /// <summary>
14 /// Message files generation class.
15 /// </summary>
16 public class GenerateMessageFiles
17 {
18 /// <summary>
19 /// Generate the message files.
20 /// </summary>
21 /// <param name="messagesDoc">Input Xml document containing message definitions.</param>
22 /// <param name="codeCompileUnit">CodeDom container.</param>
23 /// <param name="resourceWriter">Writer for default resource file.</param>
24 public static void Generate(XmlDocument messagesDoc, CodeCompileUnit codeCompileUnit, ResourceWriter resourceWriter)
25 {
26 Hashtable usedNumbers = new Hashtable();
27
28 if (null == messagesDoc)
29 {
30 throw new ArgumentNullException("messagesDoc");
31 }
32
33 if (null == codeCompileUnit)
34 {
35 throw new ArgumentNullException("codeCompileUnit");
36 }
37
38 if (null == resourceWriter)
39 {
40 throw new ArgumentNullException("resourceWriter");
41 }
42
43 string namespaceAttr = messagesDoc.DocumentElement.GetAttribute("Namespace");
44 string resourcesAttr = messagesDoc.DocumentElement.GetAttribute("Resources");
45
46 // namespace
47 CodeNamespace messagesNamespace = new CodeNamespace(namespaceAttr);
48 codeCompileUnit.Namespaces.Add(messagesNamespace);
49
50 // imports
51 messagesNamespace.Imports.Add(new CodeNamespaceImport("System"));
52 messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection"));
53 messagesNamespace.Imports.Add(new CodeNamespaceImport("System.Resources"));
54 if (namespaceAttr != "WixToolset.Data")
55 {
56 messagesNamespace.Imports.Add(new CodeNamespaceImport("WixToolset.Data"));
57 }
58
59 foreach (XmlElement classElement in messagesDoc.DocumentElement.ChildNodes)
60 {
61 string className = classElement.GetAttribute("Name");
62 string baseContainerName = classElement.GetAttribute("BaseContainerName");
63 string containerName = classElement.GetAttribute("ContainerName");
64 string messageLevel = classElement.GetAttribute("Level");
65
66 // message container class
67 messagesNamespace.Types.Add(CreateContainer(namespaceAttr, baseContainerName, containerName, messageLevel, resourcesAttr));
68
69 // class
70 CodeTypeDeclaration messagesClass = new CodeTypeDeclaration(className);
71 messagesClass.TypeAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
72 messagesNamespace.Types.Add(messagesClass);
73
74 // private constructor (needed since all methods in this class are static)
75 CodeConstructor constructor = new CodeConstructor();
76 constructor.Attributes = MemberAttributes.Private;
77 constructor.ReturnType = null;
78 messagesClass.Members.Add(constructor);
79
80 // messages
81 foreach (XmlElement messageElement in classElement.ChildNodes)
82 {
83 int number;
84 string id = messageElement.GetAttribute("Id");
85 string numberString = messageElement.GetAttribute("Number");
86 bool sourceLineNumbers = true;
87
88 // determine the message number (and ensure it was set properly)
89 if (0 < numberString.Length)
90 {
91 number = Convert.ToInt32(numberString, CultureInfo.InvariantCulture);
92 }
93 else
94 {
95 throw new ApplicationException(String.Format("Message number must be assigned for {0} '{1}'.", containerName, id));
96 }
97
98 // check for message number collisions
99 if (usedNumbers.Contains(number))
100 {
101 throw new ApplicationException(String.Format("Collision detected between two or more messages with number '{0}'.", number));
102 }
103
104 usedNumbers.Add(number, null);
105
106 if ("no" == messageElement.GetAttribute("SourceLineNumbers"))
107 {
108 sourceLineNumbers = false;
109 }
110
111 int instanceCount = 0;
112 foreach (XmlElement instanceElement in messageElement.ChildNodes)
113 {
114 string formatString = instanceElement.InnerText.Trim();
115 string resourceName = String.Concat(className, "_", id, "_", (++instanceCount).ToString());
116
117 // create a resource
118 resourceWriter.AddResource(resourceName, formatString);
119
120 // create method
121 CodeMemberMethod method = new CodeMemberMethod();
122 method.ReturnType = new CodeTypeReference(baseContainerName);
123 method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
124 messagesClass.Members.Add(method);
125
126 // method name
127 method.Name = id;
128
129 // return statement
130 CodeMethodReturnStatement stmt = new CodeMethodReturnStatement();
131 method.Statements.Add(stmt);
132
133 // return statement expression
134 CodeObjectCreateExpression expr = new CodeObjectCreateExpression();
135 stmt.Expression = expr;
136
137 // new struct
138 expr.CreateType = new CodeTypeReference(containerName);
139
140 // optionally have sourceLineNumbers as the first parameter
141 if (sourceLineNumbers)
142 {
143 // sourceLineNumbers parameter
144 expr.Parameters.Add(new CodeArgumentReferenceExpression("sourceLineNumbers"));
145 }
146 else
147 {
148 expr.Parameters.Add(new CodePrimitiveExpression(null));
149 }
150
151 // message number parameter
152 expr.Parameters.Add(new CodePrimitiveExpression(number));
153
154 // resource name parameter
155 expr.Parameters.Add(new CodePrimitiveExpression(resourceName));
156
157 // optionally have sourceLineNumbers as the first parameter
158 if (sourceLineNumbers)
159 {
160 method.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers"));
161 }
162
163 foreach (XmlNode parameterNode in instanceElement.ChildNodes)
164 {
165 XmlElement parameterElement;
166
167 if (null != (parameterElement = parameterNode as XmlElement))
168 {
169 string type = parameterElement.GetAttribute("Type");
170 string name = parameterElement.GetAttribute("Name");
171
172 // method parameter
173 method.Parameters.Add(new CodeParameterDeclarationExpression(type, name));
174
175 // String.Format parameter
176 expr.Parameters.Add(new CodeArgumentReferenceExpression(name));
177 }
178 }
179 }
180 }
181 }
182 }
183
184 /// <summary>
185 /// Create message container class.
186 /// </summary>
187 /// <param name="namespaceName">Namespace to use for resources stream.</param>
188 /// <param name="baseContainerName">Name of the base message container class.</param>
189 /// <param name="containerName">Name of the message container class.</param>
190 /// <param name="messageLevel">Message level of for the message.</param>
191 /// <param name="resourcesName">Name of the resources stream (will get namespace prepended).</param>
192 /// <returns>Message container class CodeDom object.</returns>
193 private static CodeTypeDeclaration CreateContainer(string namespaceName, string baseContainerName, string containerName, string messageLevel, string resourcesName)
194 {
195 CodeTypeDeclaration messageContainer = new CodeTypeDeclaration();
196
197 messageContainer.Name = containerName;
198 messageContainer.BaseTypes.Add(new CodeTypeReference(baseContainerName));
199
200 // constructor
201 CodeConstructor constructor = new CodeConstructor();
202 constructor.Attributes = MemberAttributes.Public;
203 constructor.ReturnType = null;
204 messageContainer.Members.Add(constructor);
205
206 CodeMemberField resourceManager = new CodeMemberField();
207 resourceManager.Attributes = MemberAttributes.Private | MemberAttributes.Static;
208 resourceManager.Name = "resourceManager";
209 resourceManager.Type = new CodeTypeReference("ResourceManager");
210 resourceManager.InitExpression = new CodeObjectCreateExpression("ResourceManager", new CodeSnippetExpression(String.Format("\"{0}.{1}\"", namespaceName, resourcesName)), new CodeSnippetExpression("Assembly.GetExecutingAssembly()"));
211 messageContainer.Members.Add(resourceManager);
212
213 // constructor parameters
214 constructor.Parameters.Add(new CodeParameterDeclarationExpression("SourceLineNumber", "sourceLineNumbers"));
215 constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "id"));
216 constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "resourceName"));
217 CodeParameterDeclarationExpression messageArgsParam = new CodeParameterDeclarationExpression("params object[]", "messageArgs");
218 constructor.Parameters.Add(messageArgsParam);
219
220 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("sourceLineNumbers"));
221 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("id"));
222 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("resourceName"));
223 constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("messageArgs"));
224
225 // assign base.Level if messageLevel is specified
226 if (!String.IsNullOrEmpty(messageLevel))
227 {
228 CodePropertyReferenceExpression levelReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "Level");
229 CodeFieldReferenceExpression messageLevelField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("MessageLevel"), messageLevel);
230 constructor.Statements.Add(new CodeAssignStatement(levelReference, messageLevelField));
231 }
232
233 // Assign base.ResourceManager property
234 CodePropertyReferenceExpression baseResourceManagerReference = new CodePropertyReferenceExpression(new CodeBaseReferenceExpression(), "ResourceManager");
235 CodeFieldReferenceExpression resourceManagerField = new CodeFieldReferenceExpression(null, "resourceManager");
236 constructor.Statements.Add(new CodeAssignStatement(baseResourceManagerReference, resourceManagerField));
237
238 //CodeMemberProperty resourceManagerProperty = new CodeMemberProperty();
239 //resourceManagerProperty.Attributes = MemberAttributes.Public | MemberAttributes.Override;
240 //resourceManagerProperty.Name = "ResourceManager";
241 //resourceManagerProperty.Type = new CodeTypeReference("ResourceManager");
242 //CodeFieldReferenceExpression resourceManagerReference = new CodeFieldReferenceExpression();
243 //resourceManagerReference.FieldName = "resourceManager";
244 //resourceManagerProperty.GetStatements.Add(new CodeMethodReturnStatement(resourceManagerReference));
245 //messageContainer.Members.Add(resourceManagerProperty);
246
247 return messageContainer;
248 }
249 }
250}