diff options
Diffstat (limited to 'src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs')
-rw-r--r-- | src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs b/src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs new file mode 100644 index 00000000..c1368190 --- /dev/null +++ b/src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs | |||
@@ -0,0 +1,863 @@ | |||
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 WixToolset.Core.ExtensibilityServices | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Diagnostics; | ||
8 | using System.Globalization; | ||
9 | using System.Linq; | ||
10 | using System.Xml; | ||
11 | using System.Xml.Linq; | ||
12 | using WixToolset.Data; | ||
13 | using WixToolset.Data.Symbols; | ||
14 | using WixToolset.Data.WindowsInstaller; | ||
15 | using WixToolset.Extensibility; | ||
16 | using WixToolset.Extensibility.Data; | ||
17 | using WixToolset.Extensibility.Services; | ||
18 | |||
19 | internal class ParseHelper : IParseHelper | ||
20 | { | ||
21 | public ParseHelper(IServiceProvider serviceProvider) | ||
22 | { | ||
23 | this.ServiceProvider = serviceProvider; | ||
24 | |||
25 | this.Messaging = serviceProvider.GetService<IMessaging>(); | ||
26 | } | ||
27 | |||
28 | private IServiceProvider ServiceProvider { get; } | ||
29 | |||
30 | private IMessaging Messaging { get; } | ||
31 | |||
32 | private ISymbolDefinitionCreator Creator { get; set; } | ||
33 | |||
34 | public bool ContainsProperty(string possibleProperty) | ||
35 | { | ||
36 | return Common.ContainsProperty(possibleProperty); | ||
37 | } | ||
38 | |||
39 | public void CreateComplexReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary) | ||
40 | { | ||
41 | |||
42 | section.AddSymbol(new WixComplexReferenceSymbol(sourceLineNumbers) | ||
43 | { | ||
44 | Parent = parentId, | ||
45 | ParentType = parentType, | ||
46 | ParentLanguage = parentLanguage, | ||
47 | Child = childId, | ||
48 | ChildType = childType, | ||
49 | IsPrimary = isPrimary | ||
50 | }); | ||
51 | |||
52 | this.CreateWixGroupSymbol(section, sourceLineNumbers, parentType, parentId, childType, childId); | ||
53 | } | ||
54 | |||
55 | public Identifier CreateDirectorySymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null) | ||
56 | { | ||
57 | if (null == id) | ||
58 | { | ||
59 | id = this.CreateIdentifier("d", parentId, name, shortName, sourceName, shortSourceName); | ||
60 | } | ||
61 | |||
62 | var symbol = section.AddSymbol(new DirectorySymbol(sourceLineNumbers, id) | ||
63 | { | ||
64 | ParentDirectoryRef = parentId, | ||
65 | Name = name, | ||
66 | ShortName = shortName, | ||
67 | SourceName = sourceName, | ||
68 | SourceShortName = shortSourceName | ||
69 | }); | ||
70 | |||
71 | return symbol.Id; | ||
72 | } | ||
73 | |||
74 | public string CreateDirectoryReferenceFromInlineSyntax(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId, string inlineSyntax, IDictionary<string, string> sectionCachedInlinedDirectoryIds) | ||
75 | { | ||
76 | if (String.IsNullOrEmpty(parentId)) | ||
77 | { | ||
78 | throw new ArgumentNullException(nameof(parentId)); | ||
79 | } | ||
80 | |||
81 | if (String.IsNullOrEmpty(inlineSyntax)) | ||
82 | { | ||
83 | inlineSyntax = this.GetAttributeLongFilename(sourceLineNumbers, attribute, false, true); | ||
84 | } | ||
85 | |||
86 | if (String.IsNullOrEmpty(inlineSyntax)) | ||
87 | { | ||
88 | return parentId; | ||
89 | } | ||
90 | |||
91 | inlineSyntax = inlineSyntax.Trim('\\', '/'); | ||
92 | |||
93 | var cacheKey = String.Concat(parentId, ":", inlineSyntax); | ||
94 | |||
95 | if (!sectionCachedInlinedDirectoryIds.TryGetValue(cacheKey, out var id)) | ||
96 | { | ||
97 | var identifier = this.CreateDirectorySymbol(section, sourceLineNumbers, id: null, parentId, inlineSyntax); | ||
98 | |||
99 | id = identifier.Id; | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, id); | ||
104 | } | ||
105 | |||
106 | return id; | ||
107 | } | ||
108 | |||
109 | public string CreateGuid(Guid namespaceGuid, string value) | ||
110 | { | ||
111 | return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant(); | ||
112 | } | ||
113 | |||
114 | public Identifier CreateIdentifier(string prefix, params string[] args) | ||
115 | { | ||
116 | var id = Common.GenerateIdentifier(prefix, args); | ||
117 | return new Identifier(AccessModifier.Section, id); | ||
118 | } | ||
119 | |||
120 | public Identifier CreateIdentifierFromFilename(string filename) | ||
121 | { | ||
122 | var id = Common.GetIdentifierFromName(filename); | ||
123 | return new Identifier(AccessModifier.Section, id); | ||
124 | } | ||
125 | |||
126 | public string CreateIdentifierValueFromPlatform(string name, Platform currentPlatform, BurnPlatforms supportedPlatforms) | ||
127 | { | ||
128 | string suffix = null; | ||
129 | |||
130 | switch (currentPlatform) | ||
131 | { | ||
132 | case Platform.X86: | ||
133 | if ((supportedPlatforms & BurnPlatforms.X86) == BurnPlatforms.X86) | ||
134 | { | ||
135 | suffix = "_X86"; | ||
136 | } | ||
137 | break; | ||
138 | case Platform.X64: | ||
139 | if ((supportedPlatforms & BurnPlatforms.X64) == BurnPlatforms.X64) | ||
140 | { | ||
141 | suffix = "_X64"; | ||
142 | } | ||
143 | break; | ||
144 | case Platform.ARM64: | ||
145 | if ((supportedPlatforms & BurnPlatforms.ARM64) == BurnPlatforms.ARM64) | ||
146 | { | ||
147 | suffix = "_A64"; | ||
148 | } | ||
149 | break; | ||
150 | } | ||
151 | |||
152 | return suffix == null ? null : name + suffix; | ||
153 | } | ||
154 | |||
155 | public Identifier CreateRegistrySymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, RegistryRootType root, string key, string name, string value, string componentId, bool escapeLeadingHash) | ||
156 | { | ||
157 | if (RegistryRootType.Unknown == root) | ||
158 | { | ||
159 | throw new ArgumentOutOfRangeException(nameof(root)); | ||
160 | } | ||
161 | |||
162 | if (null == key) | ||
163 | { | ||
164 | throw new ArgumentNullException(nameof(key)); | ||
165 | } | ||
166 | |||
167 | if (null == componentId) | ||
168 | { | ||
169 | throw new ArgumentNullException(nameof(componentId)); | ||
170 | } | ||
171 | |||
172 | // Escape the leading '#' character for string registry values. | ||
173 | if (escapeLeadingHash && null != value && value.StartsWith("#", StringComparison.Ordinal)) | ||
174 | { | ||
175 | value = String.Concat("#", value); | ||
176 | } | ||
177 | |||
178 | var id = this.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), key.ToLowerInvariant(), (null != name ? name.ToLowerInvariant() : name)); | ||
179 | |||
180 | var symbol = section.AddSymbol(new RegistrySymbol(sourceLineNumbers, id) | ||
181 | { | ||
182 | Root = root, | ||
183 | Key = key, | ||
184 | Name = name, | ||
185 | Value = value, | ||
186 | ComponentRef = componentId, | ||
187 | }); | ||
188 | |||
189 | return symbol.Id; | ||
190 | } | ||
191 | |||
192 | public void CreateSimpleReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, string symbolName, string primaryKey) | ||
193 | { | ||
194 | section.AddSymbol(new WixSimpleReferenceSymbol(sourceLineNumbers) | ||
195 | { | ||
196 | Table = symbolName, | ||
197 | PrimaryKeys = primaryKey | ||
198 | }); | ||
199 | } | ||
200 | |||
201 | public void CreateSimpleReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, string symbolName, params string[] primaryKeys) | ||
202 | { | ||
203 | section.AddSymbol(new WixSimpleReferenceSymbol(sourceLineNumbers) | ||
204 | { | ||
205 | Table = symbolName, | ||
206 | PrimaryKeys = String.Join("/", primaryKeys) | ||
207 | }); | ||
208 | } | ||
209 | |||
210 | public void CreateSimpleReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, string primaryKey) | ||
211 | { | ||
212 | this.CreateSimpleReference(section, sourceLineNumbers, symbolDefinition.Name, primaryKey); | ||
213 | } | ||
214 | |||
215 | public void CreateSimpleReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, params string[] primaryKeys) | ||
216 | { | ||
217 | this.CreateSimpleReference(section, sourceLineNumbers, symbolDefinition.Name, primaryKeys); | ||
218 | } | ||
219 | |||
220 | public void CreateWixGroupSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType childType, string childId) | ||
221 | { | ||
222 | if (null == parentId || ComplexReferenceParentType.Unknown == parentType) | ||
223 | { | ||
224 | return; | ||
225 | } | ||
226 | |||
227 | if (null == childId) | ||
228 | { | ||
229 | throw new ArgumentNullException(nameof(childId)); | ||
230 | } | ||
231 | |||
232 | section.AddSymbol(new WixGroupSymbol(sourceLineNumbers) | ||
233 | { | ||
234 | ParentId = parentId, | ||
235 | ParentType = parentType, | ||
236 | ChildId = childId, | ||
237 | ChildType = childType, | ||
238 | }); | ||
239 | } | ||
240 | |||
241 | public void CreateWixSearchSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, string elementName, Identifier id, string variable, string condition, string after, string bundleExtensionId) | ||
242 | { | ||
243 | // TODO: verify variable is not a standard bundle variable | ||
244 | if (variable == null) | ||
245 | { | ||
246 | this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, elementName, "Variable")); | ||
247 | } | ||
248 | |||
249 | section.AddSymbol(new WixSearchSymbol(sourceLineNumbers, id) | ||
250 | { | ||
251 | Variable = variable, | ||
252 | Condition = condition, | ||
253 | BundleExtensionRef = bundleExtensionId, | ||
254 | }); | ||
255 | |||
256 | if (after != null) | ||
257 | { | ||
258 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixSearch, after); | ||
259 | // TODO: We're currently defaulting to "always run after", which we will need to change... | ||
260 | this.CreateWixSearchRelationSymbol(section, sourceLineNumbers, id, after, 2); | ||
261 | } | ||
262 | |||
263 | if (!String.IsNullOrEmpty(bundleExtensionId)) | ||
264 | { | ||
265 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixBundleExtension, bundleExtensionId); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | public void CreateWixSearchRelationSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, int attributes) | ||
270 | { | ||
271 | section.AddSymbol(new WixSearchRelationSymbol(sourceLineNumbers, id) | ||
272 | { | ||
273 | ParentSearchRef = parentId, | ||
274 | Attributes = attributes, | ||
275 | }); | ||
276 | } | ||
277 | |||
278 | public IntermediateSymbol CreateSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, string symbolName, Identifier identifier = null) | ||
279 | { | ||
280 | if (this.Creator == null) | ||
281 | { | ||
282 | this.CreateSymbolDefinitionCreator(); | ||
283 | } | ||
284 | |||
285 | if (!this.Creator.TryGetSymbolDefinitionByName(symbolName, out var symbolDefinition)) | ||
286 | { | ||
287 | throw new ArgumentException(nameof(symbolName)); | ||
288 | } | ||
289 | |||
290 | return this.CreateSymbol(section, sourceLineNumbers, symbolDefinition, identifier); | ||
291 | } | ||
292 | |||
293 | public IntermediateSymbol CreateSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, Identifier identifier = null) | ||
294 | { | ||
295 | return section.AddSymbol(symbolDefinition.CreateSymbol(sourceLineNumbers, identifier)); | ||
296 | } | ||
297 | |||
298 | public void EnsureTable(IntermediateSection section, SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) | ||
299 | { | ||
300 | section.AddSymbol(new WixEnsureTableSymbol(sourceLineNumbers) | ||
301 | { | ||
302 | Table = tableDefinition.Name, | ||
303 | }); | ||
304 | |||
305 | // TODO: Check if the given table definition is a custom table. For now we have to assume that it isn't. | ||
306 | //this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixCustomTable, tableDefinition.Name); | ||
307 | } | ||
308 | |||
309 | public void EnsureTable(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName) | ||
310 | { | ||
311 | section.AddSymbol(new WixEnsureTableSymbol(sourceLineNumbers) | ||
312 | { | ||
313 | Table = tableName, | ||
314 | }); | ||
315 | |||
316 | if (this.Creator == null) | ||
317 | { | ||
318 | this.CreateSymbolDefinitionCreator(); | ||
319 | } | ||
320 | |||
321 | // TODO: The tableName may not be the same as the symbolName. For now, we have to assume that it is. | ||
322 | // We don't add custom table definitions to the tableDefinitions collection, | ||
323 | // so if it's not in there, it better be a custom table. If the Id is just wrong, | ||
324 | // instead of a custom table, we get an unresolved reference at link time. | ||
325 | if (!this.Creator.TryGetSymbolDefinitionByName(tableName, out var _)) | ||
326 | { | ||
327 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixCustomTable, tableName); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false) | ||
332 | { | ||
333 | if (null == attribute) | ||
334 | { | ||
335 | throw new ArgumentNullException(nameof(attribute)); | ||
336 | } | ||
337 | |||
338 | var emptyRule = canBeEmpty ? EmptyRule.CanBeEmpty : EmptyRule.CanBeWhitespaceOnly; | ||
339 | var value = this.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); | ||
340 | |||
341 | if (String.IsNullOrEmpty(value)) | ||
342 | { | ||
343 | if (canBeEmpty) | ||
344 | { | ||
345 | return String.Empty; | ||
346 | } | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | if (generatable && value == "*") | ||
351 | { | ||
352 | return value; | ||
353 | } | ||
354 | |||
355 | if (Guid.TryParse(value, out var guid)) | ||
356 | { | ||
357 | return guid.ToString("B").ToUpperInvariant(); | ||
358 | } | ||
359 | |||
360 | if (value.StartsWith("!(loc", StringComparison.Ordinal) || value.StartsWith("$(loc", StringComparison.Ordinal) || value.StartsWith("!(wix", StringComparison.Ordinal)) | ||
361 | { | ||
362 | return value; | ||
363 | } | ||
364 | |||
365 | if (value.StartsWith("PUT-GUID-", StringComparison.OrdinalIgnoreCase) || | ||
366 | value.StartsWith("{PUT-GUID-", StringComparison.OrdinalIgnoreCase)) | ||
367 | { | ||
368 | this.Messaging.Write(ErrorMessages.ExampleGuid(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
369 | } | ||
370 | else | ||
371 | { | ||
372 | this.Messaging.Write(ErrorMessages.IllegalGuidValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
373 | } | ||
374 | } | ||
375 | |||
376 | return CompilerConstants.IllegalGuid; | ||
377 | } | ||
378 | |||
379 | public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute) | ||
380 | { | ||
381 | var access = AccessModifier.Global; | ||
382 | var value = Common.GetAttributeValue(this.Messaging, sourceLineNumbers, attribute, EmptyRule.CanBeEmpty); | ||
383 | |||
384 | var separator = value.IndexOf(' '); | ||
385 | if (separator > 0) | ||
386 | { | ||
387 | var prefix = value.Substring(0, separator); | ||
388 | switch (prefix) | ||
389 | { | ||
390 | case "global": | ||
391 | case "public": | ||
392 | case "package": | ||
393 | access = AccessModifier.Global; | ||
394 | break; | ||
395 | |||
396 | case "internal": | ||
397 | case "library": | ||
398 | access = AccessModifier.Library; | ||
399 | break; | ||
400 | |||
401 | case "file": | ||
402 | case "protected": | ||
403 | access = AccessModifier.File; | ||
404 | break; | ||
405 | |||
406 | case "private": | ||
407 | case "fragment": | ||
408 | case "section": | ||
409 | access = AccessModifier.Section; | ||
410 | break; | ||
411 | |||
412 | default: | ||
413 | return null; | ||
414 | } | ||
415 | |||
416 | value = value.Substring(separator + 1).Trim(); | ||
417 | } | ||
418 | |||
419 | if (!Common.IsIdentifier(value)) | ||
420 | { | ||
421 | this.Messaging.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
422 | return null; | ||
423 | } | ||
424 | else if (72 < value.Length) | ||
425 | { | ||
426 | this.Messaging.Write(WarningMessages.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
427 | } | ||
428 | |||
429 | return new Identifier(access, value); | ||
430 | } | ||
431 | |||
432 | public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) | ||
433 | { | ||
434 | return Common.GetAttributeIdentifierValue(this.Messaging, sourceLineNumbers, attribute); | ||
435 | } | ||
436 | |||
437 | public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum) | ||
438 | { | ||
439 | return Common.GetAttributeIntegerValue(this.Messaging, sourceLineNumbers, attribute, minimum, maximum); | ||
440 | } | ||
441 | |||
442 | public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards, bool allowRelative) | ||
443 | { | ||
444 | if (null == attribute) | ||
445 | { | ||
446 | throw new ArgumentNullException("attribute"); | ||
447 | } | ||
448 | |||
449 | var value = this.GetAttributeValue(sourceLineNumbers, attribute); | ||
450 | |||
451 | if (!String.IsNullOrEmpty(value)) | ||
452 | { | ||
453 | if (!this.IsValidLongFilename(value, allowWildcards, allowRelative) && !this.IsValidLocIdentifier(value)) | ||
454 | { | ||
455 | if (allowRelative) | ||
456 | { | ||
457 | this.Messaging.Write(ErrorMessages.IllegalRelativeLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
458 | } | ||
459 | else | ||
460 | { | ||
461 | this.Messaging.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
462 | } | ||
463 | } | ||
464 | else if (allowRelative) | ||
465 | { | ||
466 | value = this.GetCanonicalRelativePath(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value); | ||
467 | } | ||
468 | else if (CompilerCore.IsAmbiguousFilename(value)) | ||
469 | { | ||
470 | this.Messaging.Write(WarningMessages.AmbiguousFileOrDirectoryName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
471 | } | ||
472 | } | ||
473 | |||
474 | return value; | ||
475 | } | ||
476 | |||
477 | public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum) | ||
478 | { | ||
479 | Debug.Assert(minimum > CompilerConstants.LongNotSet && minimum > CompilerConstants.IllegalLong, "The legal values for this attribute collide with at least one sentinel used during parsing."); | ||
480 | |||
481 | var value = this.GetAttributeValue(sourceLineNumbers, attribute); | ||
482 | |||
483 | if (0 < value.Length) | ||
484 | { | ||
485 | try | ||
486 | { | ||
487 | var longValue = Convert.ToInt64(value, CultureInfo.InvariantCulture.NumberFormat); | ||
488 | |||
489 | if (CompilerConstants.LongNotSet == longValue || CompilerConstants.IllegalLong == longValue) | ||
490 | { | ||
491 | this.Messaging.Write(ErrorMessages.IntegralValueSentinelCollision(sourceLineNumbers, longValue)); | ||
492 | } | ||
493 | else if (minimum > longValue || maximum < longValue) | ||
494 | { | ||
495 | this.Messaging.Write(ErrorMessages.IntegralValueOutOfRange(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, longValue, minimum, maximum)); | ||
496 | longValue = CompilerConstants.IllegalLong; | ||
497 | } | ||
498 | |||
499 | return longValue; | ||
500 | } | ||
501 | catch (FormatException) | ||
502 | { | ||
503 | this.Messaging.Write(ErrorMessages.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
504 | } | ||
505 | catch (OverflowException) | ||
506 | { | ||
507 | this.Messaging.Write(ErrorMessages.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
508 | } | ||
509 | } | ||
510 | |||
511 | return CompilerConstants.IllegalLong; | ||
512 | } | ||
513 | |||
514 | public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly) | ||
515 | { | ||
516 | return Common.GetAttributeValue(this.Messaging, sourceLineNumbers, attribute, emptyRule); | ||
517 | } | ||
518 | |||
519 | public RegistryRootType? GetAttributeRegistryRootValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowHkmu) | ||
520 | { | ||
521 | var value = this.GetAttributeValue(sourceLineNumbers, attribute); | ||
522 | if (String.IsNullOrEmpty(value)) | ||
523 | { | ||
524 | return null; | ||
525 | } | ||
526 | |||
527 | switch (value) | ||
528 | { | ||
529 | case "HKCR": | ||
530 | return RegistryRootType.ClassesRoot; | ||
531 | |||
532 | case "HKCU": | ||
533 | return RegistryRootType.CurrentUser; | ||
534 | |||
535 | case "HKLM": | ||
536 | return RegistryRootType.LocalMachine; | ||
537 | |||
538 | case "HKU": | ||
539 | return RegistryRootType.Users; | ||
540 | |||
541 | case "HKMU": | ||
542 | if (allowHkmu) | ||
543 | { | ||
544 | return RegistryRootType.MachineUser; | ||
545 | } | ||
546 | break; | ||
547 | } | ||
548 | |||
549 | if (allowHkmu) | ||
550 | { | ||
551 | this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, "HKMU", "HKCR", "HKCU", "HKLM", "HKU")); | ||
552 | } | ||
553 | else | ||
554 | { | ||
555 | this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, "HKCR", "HKCU", "HKLM", "HKU")); | ||
556 | } | ||
557 | |||
558 | return RegistryRootType.Unknown; | ||
559 | } | ||
560 | |||
561 | public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) | ||
562 | { | ||
563 | var value = this.GetAttributeValue(sourceLineNumbers, attribute); | ||
564 | |||
565 | if (!String.IsNullOrEmpty(value)) | ||
566 | { | ||
567 | if (Version.TryParse(value, out var version)) | ||
568 | { | ||
569 | return version.ToString(); | ||
570 | } | ||
571 | |||
572 | // Allow versions to contain binder variables. | ||
573 | if (Common.ContainsValidBinderVariable(value)) | ||
574 | { | ||
575 | return value; | ||
576 | } | ||
577 | |||
578 | this.Messaging.Write(ErrorMessages.IllegalVersionValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
579 | } | ||
580 | |||
581 | return null; | ||
582 | } | ||
583 | |||
584 | public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) | ||
585 | { | ||
586 | var value = this.GetAttributeValue(sourceLineNumbers, attribute); | ||
587 | |||
588 | switch (value) | ||
589 | { | ||
590 | case "yes": | ||
591 | case "true": | ||
592 | return YesNoDefaultType.Yes; | ||
593 | |||
594 | case "no": | ||
595 | case "false": | ||
596 | return YesNoDefaultType.No; | ||
597 | |||
598 | case "default": | ||
599 | return YesNoDefaultType.Default; | ||
600 | |||
601 | default: | ||
602 | this.Messaging.Write(ErrorMessages.IllegalYesNoDefaultValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
603 | return YesNoDefaultType.IllegalValue; | ||
604 | } | ||
605 | } | ||
606 | |||
607 | public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) | ||
608 | { | ||
609 | var value = this.GetAttributeValue(sourceLineNumbers, attribute); | ||
610 | |||
611 | switch (value) | ||
612 | { | ||
613 | case "yes": | ||
614 | case "true": | ||
615 | return YesNoType.Yes; | ||
616 | |||
617 | case "no": | ||
618 | case "false": | ||
619 | return YesNoType.No; | ||
620 | |||
621 | default: | ||
622 | this.Messaging.Write(ErrorMessages.IllegalYesNoValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value)); | ||
623 | return YesNoType.IllegalValue; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath) | ||
628 | { | ||
629 | return Common.GetCanonicalRelativePath(sourceLineNumbers, elementName, attributeName, relativePath, this.Messaging); | ||
630 | } | ||
631 | |||
632 | public SourceLineNumber GetSourceLineNumbers(XElement element) | ||
633 | { | ||
634 | return Preprocessor.GetSourceLineNumbers(element); | ||
635 | } | ||
636 | |||
637 | public string GetConditionInnerText(XElement element) | ||
638 | { | ||
639 | var value = Common.GetInnerText(element)?.Trim().Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' '); | ||
640 | |||
641 | // Return null for a non-existant condition. | ||
642 | return String.IsNullOrEmpty(value) ? null : value; | ||
643 | } | ||
644 | |||
645 | public string GetTrimmedInnerText(XElement element) | ||
646 | { | ||
647 | var value = Common.GetInnerText(element); | ||
648 | return value?.Trim(); | ||
649 | } | ||
650 | |||
651 | public void InnerTextDisallowed(XElement element) | ||
652 | { | ||
653 | if (element.Nodes().Any(n => XmlNodeType.Text == n.NodeType || XmlNodeType.CDATA == n.NodeType)) | ||
654 | { | ||
655 | var innerText = Common.GetInnerText(element); | ||
656 | if (!String.IsNullOrWhiteSpace(innerText)) | ||
657 | { | ||
658 | var sourceLineNumbers = this.GetSourceLineNumbers(element); | ||
659 | this.Messaging.Write(ErrorMessages.IllegalInnerText(sourceLineNumbers, element.Name.LocalName, innerText)); | ||
660 | } | ||
661 | } | ||
662 | } | ||
663 | |||
664 | public bool IsValidIdentifier(string value) | ||
665 | { | ||
666 | return Common.IsIdentifier(value); | ||
667 | } | ||
668 | |||
669 | public bool IsValidLocIdentifier(string identifier) | ||
670 | { | ||
671 | return Common.TryParseWixVariable(identifier, 0, out var parsed) && parsed.Index == 0 && parsed.Length == identifier.Length && parsed.Namespace == "loc"; | ||
672 | } | ||
673 | |||
674 | public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) | ||
675 | { | ||
676 | return Common.IsValidLongFilename(filename, allowWildcards, allowRelative); | ||
677 | } | ||
678 | |||
679 | public bool IsValidShortFilename(string filename, bool allowWildcards) | ||
680 | { | ||
681 | return Common.IsValidShortFilename(filename, allowWildcards); | ||
682 | } | ||
683 | |||
684 | public void ParseExtensionAttribute(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element, XAttribute attribute, IDictionary<string, string> context = null) | ||
685 | { | ||
686 | // Ignore attributes defined by the W3C because we'll assume they are always right. | ||
687 | if ((String.IsNullOrEmpty(attribute.Name.NamespaceName) && attribute.Name.LocalName.Equals("xmlns", StringComparison.Ordinal)) || | ||
688 | attribute.Name.NamespaceName.StartsWith(CompilerCore.W3SchemaPrefix.NamespaceName, StringComparison.Ordinal)) | ||
689 | { | ||
690 | return; | ||
691 | } | ||
692 | |||
693 | if (ParseHelper.TryFindExtension(extensions, attribute.Name.NamespaceName, out var extension)) | ||
694 | { | ||
695 | extension.ParseAttribute(intermediate, section, element, attribute, context); | ||
696 | } | ||
697 | else | ||
698 | { | ||
699 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); | ||
700 | this.Messaging.Write(ErrorMessages.UnhandledExtensionAttribute(sourceLineNumbers, element.Name.LocalName, attribute.Name.LocalName, attribute.Name.NamespaceName)); | ||
701 | } | ||
702 | } | ||
703 | |||
704 | public void ParseExtensionElement(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context = null) | ||
705 | { | ||
706 | if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension)) | ||
707 | { | ||
708 | extension.ParseElement(intermediate, section, parentElement, element, context); | ||
709 | } | ||
710 | else | ||
711 | { | ||
712 | var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); | ||
713 | this.Messaging.Write(ErrorMessages.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName)); | ||
714 | } | ||
715 | } | ||
716 | |||
717 | public IComponentKeyPath ParsePossibleKeyPathExtensionElement(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context) | ||
718 | { | ||
719 | IComponentKeyPath keyPath = null; | ||
720 | |||
721 | if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension)) | ||
722 | { | ||
723 | keyPath = extension.ParsePossibleKeyPathElement(intermediate, section, parentElement, element, context); | ||
724 | } | ||
725 | else | ||
726 | { | ||
727 | var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); | ||
728 | this.Messaging.Write(ErrorMessages.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName)); | ||
729 | } | ||
730 | |||
731 | return keyPath; | ||
732 | } | ||
733 | |||
734 | public void ParseForExtensionElements(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element) | ||
735 | { | ||
736 | var checkInnerText = false; | ||
737 | |||
738 | foreach (var child in element.Nodes()) | ||
739 | { | ||
740 | if (child is XElement childElement) | ||
741 | { | ||
742 | if (element.Name.Namespace == childElement.Name.Namespace) | ||
743 | { | ||
744 | this.UnexpectedElement(element, childElement); | ||
745 | } | ||
746 | else | ||
747 | { | ||
748 | this.ParseExtensionElement(extensions, intermediate, section, element, childElement); | ||
749 | } | ||
750 | } | ||
751 | else | ||
752 | { | ||
753 | checkInnerText = true; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | if (checkInnerText) | ||
758 | { | ||
759 | this.InnerTextDisallowed(element); | ||
760 | } | ||
761 | } | ||
762 | |||
763 | public WixActionSymbol ScheduleActionSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, AccessModifier access, SequenceTable sequence, string actionName, string condition, string beforeAction, string afterAction, bool overridable = false) | ||
764 | { | ||
765 | var actionId = new Identifier(access, sequence, actionName); | ||
766 | |||
767 | var actionSymbol = section.AddSymbol(new WixActionSymbol(sourceLineNumbers, actionId) | ||
768 | { | ||
769 | SequenceTable = sequence, | ||
770 | Action = actionName, | ||
771 | Condition = condition, | ||
772 | Before = beforeAction, | ||
773 | After = afterAction, | ||
774 | Overridable = overridable, | ||
775 | }); | ||
776 | |||
777 | if (null != beforeAction) | ||
778 | { | ||
779 | if (WindowsInstallerStandard.IsStandardAction(beforeAction)) | ||
780 | { | ||
781 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixAction, sequence.ToString(), beforeAction); | ||
782 | } | ||
783 | else | ||
784 | { | ||
785 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.CustomAction, beforeAction); | ||
786 | } | ||
787 | } | ||
788 | |||
789 | if (null != afterAction) | ||
790 | { | ||
791 | if (WindowsInstallerStandard.IsStandardAction(afterAction)) | ||
792 | { | ||
793 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixAction, sequence.ToString(), afterAction); | ||
794 | } | ||
795 | else | ||
796 | { | ||
797 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.CustomAction, afterAction); | ||
798 | } | ||
799 | } | ||
800 | |||
801 | return actionSymbol; | ||
802 | } | ||
803 | |||
804 | public void CreateCustomActionReference(SourceLineNumber sourceLineNumbers, IntermediateSection section, string customAction, Platform currentPlatform, CustomActionPlatforms supportedPlatforms) | ||
805 | { | ||
806 | if (!this.Messaging.EncounteredError) | ||
807 | { | ||
808 | var suffix = "_X86"; | ||
809 | |||
810 | switch (currentPlatform) | ||
811 | { | ||
812 | case Platform.X64: | ||
813 | if ((supportedPlatforms & CustomActionPlatforms.X64) == CustomActionPlatforms.X64) | ||
814 | { | ||
815 | suffix = "_X64"; | ||
816 | } | ||
817 | break; | ||
818 | case Platform.ARM64: | ||
819 | if ((supportedPlatforms & CustomActionPlatforms.ARM64) == CustomActionPlatforms.ARM64) | ||
820 | { | ||
821 | suffix = "_A64"; | ||
822 | } | ||
823 | break; | ||
824 | } | ||
825 | |||
826 | this.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.CustomAction, customAction + suffix); | ||
827 | } | ||
828 | } | ||
829 | |||
830 | public void UnexpectedAttribute(XElement element, XAttribute attribute) | ||
831 | { | ||
832 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); | ||
833 | Common.UnexpectedAttribute(this.Messaging, sourceLineNumbers, attribute); | ||
834 | } | ||
835 | |||
836 | public void UnexpectedElement(XElement parentElement, XElement childElement) | ||
837 | { | ||
838 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(childElement); | ||
839 | this.Messaging.Write(ErrorMessages.UnexpectedElement(sourceLineNumbers, parentElement.Name.LocalName, childElement.Name.LocalName)); | ||
840 | } | ||
841 | |||
842 | private void CreateSymbolDefinitionCreator() | ||
843 | { | ||
844 | this.Creator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>(); | ||
845 | } | ||
846 | |||
847 | private static bool TryFindExtension(IEnumerable<ICompilerExtension> extensions, XNamespace ns, out ICompilerExtension extension) | ||
848 | { | ||
849 | extension = null; | ||
850 | |||
851 | foreach (var ext in extensions) | ||
852 | { | ||
853 | if (ext.Namespace == ns) | ||
854 | { | ||
855 | extension = ext; | ||
856 | break; | ||
857 | } | ||
858 | } | ||
859 | |||
860 | return extension != null; | ||
861 | } | ||
862 | } | ||
863 | } | ||