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