diff options
Diffstat (limited to 'src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs')
-rw-r--r-- | src/internal/WixBuildTools.MsgGen/GenerateMessageFiles.cs | 250 |
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 | |||
3 | namespace 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 | } | ||