aboutsummaryrefslogtreecommitdiff
path: root/src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs')
-rw-r--r--src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs863
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
3namespace 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}