aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-11-14 23:21:52 -0800
committerRob Mensching <rob@firegiant.com>2017-11-14 23:21:52 -0800
commitaa6f15ec4998a77622fafe9c510b3b547c595679 (patch)
tree50249c24429424ee14531a2047954c9df1ee345a /src/WixToolset.Core
parentb6bf1604c32259757f75b4c35444cfe4ecc21a86 (diff)
downloadwix-aa6f15ec4998a77622fafe9c510b3b547c595679.tar.gz
wix-aa6f15ec4998a77622fafe9c510b3b547c595679.tar.bz2
wix-aa6f15ec4998a77622fafe9c510b3b547c595679.zip
Refactor CompilerCore to ParserHelper and other clean up
Diffstat (limited to 'src/WixToolset.Core')
-rw-r--r--src/WixToolset.Core/AppCommon.cs3
-rw-r--r--src/WixToolset.Core/BinderCore.cs44
-rw-r--r--src/WixToolset.Core/Common.cs7
-rw-r--r--src/WixToolset.Core/Compiler.cs13
-rw-r--r--src/WixToolset.Core/CompilerCore.cs760
-rw-r--r--src/WixToolset.Core/Converter.cs2
-rw-r--r--src/WixToolset.Core/Extensibility/HeatExtension.cs2
-rw-r--r--src/WixToolset.Core/Extensibility/IHeatCore.cs2
-rw-r--r--src/WixToolset.Core/Extensibility/MutatorExtension.cs2
-rw-r--r--src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs824
-rw-r--r--src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs (renamed from src/WixToolset.Core/TupleDefinitionCreator.cs)2
-rw-r--r--src/WixToolset.Core/HarvesterCore.cs3
-rw-r--r--src/WixToolset.Core/HeatCore.cs4
-rw-r--r--src/WixToolset.Core/Link/WixGroupingOrdering.cs4
-rw-r--r--src/WixToolset.Core/Localizer.cs2
-rw-r--r--src/WixToolset.Core/Mutator.cs4
-rw-r--r--src/WixToolset.Core/Preprocessor.cs3
-rw-r--r--src/WixToolset.Core/WixToolsetServiceProvider.cs7
18 files changed, 946 insertions, 742 deletions
diff --git a/src/WixToolset.Core/AppCommon.cs b/src/WixToolset.Core/AppCommon.cs
index 5fde632a..7716155a 100644
--- a/src/WixToolset.Core/AppCommon.cs
+++ b/src/WixToolset.Core/AppCommon.cs
@@ -1,10 +1,9 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Specialized; 6 using System.Collections.Specialized;
7 using System.Configuration;
8 using System.Globalization; 7 using System.Globalization;
9 using System.IO; 8 using System.IO;
10 using System.Reflection; 9 using System.Reflection;
diff --git a/src/WixToolset.Core/BinderCore.cs b/src/WixToolset.Core/BinderCore.cs
deleted file mode 100644
index 84173b99..00000000
--- a/src/WixToolset.Core/BinderCore.cs
+++ /dev/null
@@ -1,44 +0,0 @@
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
4{
5 using WixToolset.Data;
6 using WixToolset.Extensibility;
7
8 /// <summary>
9 /// Core class for the binder.
10 /// </summary>
11 internal class BinderCore : IBinderCore
12 {
13 public IBinderFileManagerCore FileManagerCore { get; set; }
14
15 /// <summary>
16 /// Gets whether the binder core encountered an error while processing.
17 /// </summary>
18 /// <value>Flag if core encountered an error during processing.</value>
19 public bool EncounteredError
20 {
21 get { return Messaging.Instance.EncounteredError; }
22 }
23
24 /// <summary>
25 /// Generate an identifier by hashing data from the row.
26 /// </summary>
27 /// <param name="prefix">Three letter or less prefix for generated row identifier.</param>
28 /// <param name="args">Information to hash.</param>
29 /// <returns>The generated identifier.</returns>
30 public string CreateIdentifier(string prefix, params string[] args)
31 {
32 return Common.GenerateIdentifier(prefix, args);
33 }
34
35 /// <summary>
36 /// Sends a message to the message delegate if there is one.
37 /// </summary>
38 /// <param name="mea">Message event arguments.</param>
39 public void OnMessage(MessageEventArgs e)
40 {
41 Messaging.Instance.OnMessage(e);
42 }
43 }
44}
diff --git a/src/WixToolset.Core/Common.cs b/src/WixToolset.Core/Common.cs
index 28e7ee7b..9a0d3aec 100644
--- a/src/WixToolset.Core/Common.cs
+++ b/src/WixToolset.Core/Common.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Diagnostics; 6 using System.Diagnostics;
@@ -13,6 +13,7 @@ namespace WixToolset
13 using System.Xml; 13 using System.Xml;
14 using System.Xml.Linq; 14 using System.Xml.Linq;
15 using WixToolset.Data; 15 using WixToolset.Data;
16 using WixToolset.Extensibility;
16 17
17 /// <summary> 18 /// <summary>
18 /// Common Wix utility methods and types. 19 /// Common Wix utility methods and types.
@@ -766,8 +767,8 @@ namespace WixToolset
766 /// <returns>The attribute's YesNoType value.</returns> 767 /// <returns>The attribute's YesNoType value.</returns>
767 internal static string GetInnerText(XElement node) 768 internal static string GetInnerText(XElement node)
768 { 769 {
769 XText text = node.Nodes().Where(n => XmlNodeType.Text == n.NodeType || XmlNodeType.CDATA == n.NodeType).Cast<XText>().FirstOrDefault(); 770 var text = node.Nodes().Where(n => XmlNodeType.Text == n.NodeType || XmlNodeType.CDATA == n.NodeType).Cast<XText>().FirstOrDefault();
770 return (null == text) ? null : text.Value; 771 return text?.Value;
771 } 772 }
772 773
773 /// <summary> 774 /// <summary>
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs
index 903aae61..4b1ef033 100644
--- a/src/WixToolset.Core/Compiler.cs
+++ b/src/WixToolset.Core/Compiler.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
@@ -11,7 +11,6 @@ namespace WixToolset
11 using System.IO; 11 using System.IO;
12 using System.Text.RegularExpressions; 12 using System.Text.RegularExpressions;
13 using System.Xml.Linq; 13 using System.Xml.Linq;
14 using WixToolset.Core;
15 using WixToolset.Core.Native; 14 using WixToolset.Core.Native;
16 using WixToolset.Data; 15 using WixToolset.Data;
17 using WixToolset.Data.Tuples; 16 using WixToolset.Data.Tuples;
@@ -22,7 +21,6 @@ namespace WixToolset
22 /// <summary> 21 /// <summary>
23 /// Compiler of the WiX toolset. 22 /// Compiler of the WiX toolset.
24 /// </summary> 23 /// </summary>
25 [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
26 public sealed class Compiler 24 public sealed class Compiler
27 { 25 {
28 public const string UpgradeDetectedProperty = "WIX_UPGRADE_DETECTED"; 26 public const string UpgradeDetectedProperty = "WIX_UPGRADE_DETECTED";
@@ -40,7 +38,7 @@ namespace WixToolset
40 private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder"; 38 private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder";
41 private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource"; 39 private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource";
42 40
43 // if these are true you know you are building a module or product 41 // If these are true you know you are building a module or product
44 // but if they are false you cannot not be sure they will not end 42 // but if they are false you cannot not be sure they will not end
45 // up a product or module. Use these flags carefully. 43 // up a product or module. Use these flags carefully.
46 private bool compilingModule; 44 private bool compilingModule;
@@ -123,16 +121,15 @@ namespace WixToolset
123 // Try to compile it. 121 // Try to compile it.
124 try 122 try
125 { 123 {
126 var creator = context.ServiceProvider.GetService<ITupleDefinitionCreator>(); 124 var parseHelper = context.ServiceProvider.GetService<IParseHelper>();
127 125
128 this.Core = new CompilerCore(target, creator, extensionsByNamespace); 126 this.Core = new CompilerCore(target, parseHelper, extensionsByNamespace);
129 this.Core.CurrentPlatform = this.Context.Platform;
130 this.Core.ShowPedanticMessages = this.ShowPedanticMessages; 127 this.Core.ShowPedanticMessages = this.ShowPedanticMessages;
131 this.componentIdPlaceholdersResolver = new WixVariableResolver(); 128 this.componentIdPlaceholdersResolver = new WixVariableResolver();
132 129
133 // parse the document 130 // parse the document
134 var source = context.Source; 131 var source = context.Source;
135 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(source.Root); 132 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(source.Root);
136 if ("Wix" == source.Root.Name.LocalName) 133 if ("Wix" == source.Root.Name.LocalName)
137 { 134 {
138 if (CompilerCore.WixNamespace == source.Root.Name.Namespace) 135 if (CompilerCore.WixNamespace == source.Root.Name.Namespace)
diff --git a/src/WixToolset.Core/CompilerCore.cs b/src/WixToolset.Core/CompilerCore.cs
index 46a2e435..26c19acc 100644
--- a/src/WixToolset.Core/CompilerCore.cs
+++ b/src/WixToolset.Core/CompilerCore.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
@@ -17,6 +17,7 @@ namespace WixToolset
17 using WixToolset.Data; 17 using WixToolset.Data;
18 using WixToolset.Data.Tuples; 18 using WixToolset.Data.Tuples;
19 using WixToolset.Extensibility; 19 using WixToolset.Extensibility;
20 using WixToolset.Extensibility.Services;
20 using Wix = WixToolset.Data.Serialize; 21 using Wix = WixToolset.Data.Serialize;
21 22
22 internal enum ValueListKind 23 internal enum ValueListKind
@@ -40,7 +41,7 @@ namespace WixToolset
40 /// <summary> 41 /// <summary>
41 /// Core class for the compiler. 42 /// Core class for the compiler.
42 /// </summary> 43 /// </summary>
43 internal sealed class CompilerCore //: ICompilerCore 44 internal sealed class CompilerCore
44 { 45 {
45 internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/"; 46 internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/";
46 internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; 47 internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs";
@@ -50,19 +51,6 @@ namespace WixToolset
50 private const string IllegalLongFilenameCharacters = @"[\\\?|><:/\*""]"; // illegal: \ ? | > < : / * " 51 private const string IllegalLongFilenameCharacters = @"[\\\?|><:/\*""]"; // illegal: \ ? | > < : / * "
51 private static readonly Regex IllegalLongFilename = new Regex(IllegalLongFilenameCharacters, RegexOptions.Compiled); 52 private static readonly Regex IllegalLongFilename = new Regex(IllegalLongFilenameCharacters, RegexOptions.Compiled);
52 53
53 private const string LegalLongFilenameCharacters = @"[^\\\?|><:/\*""]"; // opposite of illegal above.
54 private static readonly Regex LegalLongFilename = new Regex(String.Concat("^", LegalLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled);
55
56 private const string LegalRelativeLongFilenameCharacters = @"[^\?|><:/\*""]"; // (like legal long, but we allow '\') illegal: ? | > < : / * "
57 private static readonly Regex LegalRelativeLongFilename = new Regex(String.Concat("^", LegalRelativeLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled);
58
59 private const string LegalWildcardLongFilenameCharacters = @"[^\\|><:/""]"; // illegal: \ | > < : / "
60 private static readonly Regex LegalWildcardLongFilename = new Regex(String.Concat("^", LegalWildcardLongFilenameCharacters, @"{1,259}$"));
61
62 private static readonly Regex PutGuidHere = new Regex(@"PUT\-GUID\-(?:\d+\-)?HERE", RegexOptions.Singleline);
63
64 private static readonly Regex LegalIdentifierWithAccess = new Regex(@"^((?<access>public|internal|protected|private)\s+)?(?<id>[_A-Za-z][0-9A-Za-z_\.]*)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
65
66 public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB 54 public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB
67 public const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB 55 public const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB
68 public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) 56 public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB)
@@ -141,7 +129,7 @@ namespace WixToolset
141 }); 129 });
142 130
143 private Dictionary<XNamespace, ICompilerExtension> extensions; 131 private Dictionary<XNamespace, ICompilerExtension> extensions;
144 private ITupleDefinitionCreator creator; 132 private IParseHelper parseHelper;
145 private Intermediate intermediate; 133 private Intermediate intermediate;
146 134
147 private HashSet<string> activeSectionInlinedDirectoryIds; 135 private HashSet<string> activeSectionInlinedDirectoryIds;
@@ -152,10 +140,10 @@ namespace WixToolset
152 /// </summary> 140 /// </summary>
153 /// <param name="intermediate">The Intermediate object representing compiled source document.</param> 141 /// <param name="intermediate">The Intermediate object representing compiled source document.</param>
154 /// <param name="extensions">The WiX extensions collection.</param> 142 /// <param name="extensions">The WiX extensions collection.</param>
155 internal CompilerCore(Intermediate intermediate, ITupleDefinitionCreator creator, Dictionary<XNamespace, ICompilerExtension> extensions) 143 internal CompilerCore(Intermediate intermediate, IParseHelper parseHelper, Dictionary<XNamespace, ICompilerExtension> extensions)
156 { 144 {
157 this.extensions = extensions; 145 this.extensions = extensions;
158 this.creator = creator; 146 this.parseHelper = parseHelper;
159 this.intermediate = intermediate; 147 this.intermediate = intermediate;
160 } 148 }
161 149
@@ -166,12 +154,6 @@ namespace WixToolset
166 public IntermediateSection ActiveSection { get; private set; } 154 public IntermediateSection ActiveSection { get; private set; }
167 155
168 /// <summary> 156 /// <summary>
169 /// Gets or sets the platform which the compiler will use when defaulting 64-bit attributes and elements.
170 /// </summary>
171 /// <value>The platform which the compiler will use when defaulting 64-bit attributes and elements.</value>
172 public Platform CurrentPlatform { get; set; }
173
174 /// <summary>
175 /// Gets whether the compiler core encountered an error while processing. 157 /// Gets whether the compiler core encountered an error while processing.
176 /// </summary> 158 /// </summary>
177 /// <value>Flag if core encountered an error during processing.</value> 159 /// <value>Flag if core encountered an error during processing.</value>
@@ -231,12 +213,7 @@ namespace WixToolset
231 /// <returns>true if the filename is ambiguous; false otherwise.</returns> 213 /// <returns>true if the filename is ambiguous; false otherwise.</returns>
232 public static bool IsAmbiguousFilename(string filename) 214 public static bool IsAmbiguousFilename(string filename)
233 { 215 {
234 if (null == filename || 0 == filename.Length) 216 return String.IsNullOrEmpty(filename) ? false : CompilerCore.AmbiguousFilename.IsMatch(filename);
235 {
236 return false;
237 }
238
239 return CompilerCore.AmbiguousFilename.IsMatch(filename);
240 } 217 }
241 218
242 /// <summary> 219 /// <summary>
@@ -246,7 +223,7 @@ namespace WixToolset
246 /// <returns>true if the value is an identifier; false otherwise.</returns> 223 /// <returns>true if the value is an identifier; false otherwise.</returns>
247 public bool IsValidIdentifier(string value) 224 public bool IsValidIdentifier(string value)
248 { 225 {
249 return Common.IsIdentifier(value); 226 return this.parseHelper.IsValidIdentifier(value);
250 } 227 }
251 228
252 /// <summary> 229 /// <summary>
@@ -256,14 +233,7 @@ namespace WixToolset
256 /// <returns>True if the identifier is a valid loc identifier.</returns> 233 /// <returns>True if the identifier is a valid loc identifier.</returns>
257 public bool IsValidLocIdentifier(string identifier) 234 public bool IsValidLocIdentifier(string identifier)
258 { 235 {
259 if (String.IsNullOrEmpty(identifier)) 236 return this.parseHelper.IsValidIdentifier(identifier);
260 {
261 return false;
262 }
263
264 Match match = Common.WixVariableRegex.Match(identifier);
265
266 return (match.Success && "loc" == match.Groups["namespace"].Value && 0 == match.Index && identifier.Length == match.Length);
267 } 237 }
268 238
269 /// <summary> 239 /// <summary>
@@ -275,34 +245,7 @@ namespace WixToolset
275 /// <returns>True if the filename is a valid long filename</returns> 245 /// <returns>True if the filename is a valid long filename</returns>
276 public bool IsValidLongFilename(string filename, bool allowWildcards = false, bool allowRelative = false) 246 public bool IsValidLongFilename(string filename, bool allowWildcards = false, bool allowRelative = false)
277 { 247 {
278 if (String.IsNullOrEmpty(filename)) 248 return this.parseHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
279 {
280 return false;
281 }
282
283 // check for a non-period character (all periods is not legal)
284 bool nonPeriodFound = false;
285 foreach (char character in filename)
286 {
287 if ('.' != character)
288 {
289 nonPeriodFound = true;
290 break;
291 }
292 }
293
294 if (allowWildcards)
295 {
296 return (nonPeriodFound && CompilerCore.LegalWildcardLongFilename.IsMatch(filename));
297 }
298 else if (allowRelative)
299 {
300 return (nonPeriodFound && CompilerCore.LegalRelativeLongFilename.IsMatch(filename));
301 }
302 else
303 {
304 return (nonPeriodFound && CompilerCore.LegalLongFilename.IsMatch(filename));
305 }
306 } 249 }
307 250
308 /// <summary> 251 /// <summary>
@@ -313,7 +256,7 @@ namespace WixToolset
313 /// <returns>True if the filename is a valid short filename</returns> 256 /// <returns>True if the filename is a valid short filename</returns>
314 public bool IsValidShortFilename(string filename, bool allowWildcards) 257 public bool IsValidShortFilename(string filename, bool allowWildcards)
315 { 258 {
316 return Common.IsValidShortFilename(filename, allowWildcards); 259 return this.parseHelper.IsValidShortFilename(filename, allowWildcards);
317 } 260 }
318 261
319 /// <summary> 262 /// <summary>
@@ -337,52 +280,7 @@ namespace WixToolset
337 /// <returns>The generated 8.3-compliant short file/directory name.</returns> 280 /// <returns>The generated 8.3-compliant short file/directory name.</returns>
338 public string CreateShortName(string longName, bool keepExtension, bool allowWildcards, params string[] args) 281 public string CreateShortName(string longName, bool keepExtension, bool allowWildcards, params string[] args)
339 { 282 {
340 // canonicalize the long name if its not a localization identifier (they are case-sensitive) 283 return this.parseHelper.CreateShortName(longName, keepExtension, allowWildcards, args);
341 if (!this.IsValidLocIdentifier(longName))
342 {
343 longName = longName.ToLowerInvariant();
344 }
345
346 // collect all the data
347 List<string> strings = new List<string>(1 + args.Length);
348 strings.Add(longName);
349 strings.AddRange(args);
350
351 // prepare for hashing
352 string stringData = String.Join("|", strings);
353 byte[] data = Encoding.UTF8.GetBytes(stringData);
354
355 // hash the data
356 byte[] hash;
357 using (SHA1 sha1 = new SHA1CryptoServiceProvider())
358 {
359 hash = sha1.ComputeHash(data);
360 }
361
362 // generate the short file/directory name without an extension
363 StringBuilder shortName = new StringBuilder(Convert.ToBase64String(hash));
364 shortName.Remove(8, shortName.Length - 8).Replace('+', '-').Replace('/', '_');
365
366 if (keepExtension)
367 {
368 string extension = Path.GetExtension(longName);
369
370 if (4 < extension.Length)
371 {
372 extension = extension.Substring(0, 4);
373 }
374
375 shortName.Append(extension);
376
377 // check the generated short name to ensure its still legal (the extension may not be legal)
378 if (!this.IsValidShortFilename(shortName.ToString(), allowWildcards))
379 {
380 // remove the extension (by truncating the generated file name back to the generated characters)
381 shortName.Length -= extension.Length;
382 }
383 }
384
385 return shortName.ToString().ToLowerInvariant();
386 } 284 }
387 285
388 /// <summary> 286 /// <summary>
@@ -422,8 +320,7 @@ namespace WixToolset
422 /// <returns>The node's inner text trimmed.</returns> 320 /// <returns>The node's inner text trimmed.</returns>
423 public string GetTrimmedInnerText(XElement element) 321 public string GetTrimmedInnerText(XElement element)
424 { 322 {
425 string value = Common.GetInnerText(element); 323 return this.parseHelper.GetTrimmedInnerText(element);
426 return (null == value) ? null : value.Trim();
427 } 324 }
428 325
429 /// <summary> 326 /// <summary>
@@ -431,23 +328,9 @@ namespace WixToolset
431 /// </summary> 328 /// </summary>
432 /// <param name="element">The element to ensure inner text is a condition.</param> 329 /// <param name="element">The element to ensure inner text is a condition.</param>
433 /// <returns>The value converted into a safe condition.</returns> 330 /// <returns>The value converted into a safe condition.</returns>
434 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
435 public string GetConditionInnerText(XElement element) 331 public string GetConditionInnerText(XElement element)
436 { 332 {
437 string value = element.Value; 333 return this.parseHelper.GetConditionInnerText(element);
438 if (0 < value.Length)
439 {
440 value = value.Trim();
441 value = value.Replace('\t', ' ');
442 value = value.Replace('\r', ' ');
443 value = value.Replace('\n', ' ');
444 }
445 else // return null for a non-existant condition
446 {
447 value = null;
448 }
449
450 return value;
451 } 334 }
452 335
453 /// <summary> 336 /// <summary>
@@ -458,7 +341,7 @@ namespace WixToolset
458 /// <returns>The generated GUID for the given namespace and value.</returns> 341 /// <returns>The generated GUID for the given namespace and value.</returns>
459 public string CreateGuid(Guid namespaceGuid, string value) 342 public string CreateGuid(Guid namespaceGuid, string value)
460 { 343 {
461 return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant(); 344 return this.parseHelper.CreateGuid(namespaceGuid, value);
462 } 345 }
463 346
464 /// <summary> 347 /// <summary>
@@ -510,46 +393,7 @@ namespace WixToolset
510 /// <returns>Identifier of the leaf directory created.</returns> 393 /// <returns>Identifier of the leaf directory created.</returns>
511 public string CreateDirectoryReferenceFromInlineSyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId) 394 public string CreateDirectoryReferenceFromInlineSyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId)
512 { 395 {
513 string id = null; 396 return this.parseHelper.CreateDirectoryReferenceFromInlineSyntax(this.ActiveSection, sourceLineNumbers, attribute, parentId);
514 string[] inlineSyntax = this.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, true);
515
516 if (null != inlineSyntax)
517 {
518 // Special case the single entry in the inline syntax since it is the most common case
519 // and needs no extra processing. It's just a reference to an existing directory.
520 if (1 == inlineSyntax.Length)
521 {
522 id = inlineSyntax[0];
523 this.CreateSimpleReference(sourceLineNumbers, "Directory", id);
524 }
525 else // start creating rows for the entries in the inline syntax
526 {
527 id = parentId;
528
529 int pathStartsAt = 0;
530 if (inlineSyntax[0].EndsWith(":"))
531 {
532 // TODO: should overriding the parent identifier with a specific id be an error or a warning or just let it slide?
533 //if (null != parentId)
534 //{
535 // this.core.OnMessage(WixErrors.Xxx(sourceLineNumbers));
536 //}
537
538 id = inlineSyntax[0].TrimEnd(':');
539 this.CreateSimpleReference(sourceLineNumbers, "Directory", id);
540
541 pathStartsAt = 1;
542 }
543
544 for (int i = pathStartsAt; i < inlineSyntax.Length; ++i)
545 {
546 Identifier inlineId = this.CreateDirectoryRow(sourceLineNumbers, null, id, inlineSyntax[i]);
547 id = inlineId.Id;
548 }
549 }
550 }
551
552 return id;
553 } 397 }
554 398
555 /// <summary> 399 /// <summary>
@@ -575,59 +419,9 @@ namespace WixToolset
575 /// <param name="name">The registry entry name.</param> 419 /// <param name="name">The registry entry name.</param>
576 /// <param name="value">The registry entry value.</param> 420 /// <param name="value">The registry entry value.</param>
577 /// <param name="componentId">The component which will control installation/uninstallation of the registry entry.</param> 421 /// <param name="componentId">The component which will control installation/uninstallation of the registry entry.</param>
578 /// <param name="escapeLeadingHash">If true, "escape" leading '#' characters so the value is written as a REG_SZ.</param>
579 public Identifier CreateRegistryRow(SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId, bool escapeLeadingHash)
580 {
581 Identifier id = null;
582
583 if (!this.EncounteredError)
584 {
585 if (-1 > root || 3 < root)
586 {
587 throw new ArgumentOutOfRangeException("root");
588 }
589
590 if (null == key)
591 {
592 throw new ArgumentNullException("key");
593 }
594
595 if (null == componentId)
596 {
597 throw new ArgumentNullException("componentId");
598 }
599
600 // escape the leading '#' character for string registry values
601 if (escapeLeadingHash && null != value && value.StartsWith("#", StringComparison.Ordinal))
602 {
603 value = String.Concat("#", value);
604 }
605
606 id = this.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), key.ToLowerInvariant(), (null != name ? name.ToLowerInvariant() : name));
607
608 var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.Registry, id);
609 row.Set(1, root);
610 row.Set(2, key);
611 row.Set(3, name);
612 row.Set(4, value);
613 row.Set(5, componentId);
614 }
615
616 return id;
617 }
618
619 /// <summary>
620 /// Creates a Registry row in the active section.
621 /// </summary>
622 /// <param name="sourceLineNumbers">Source and line number of the current row.</param>
623 /// <param name="root">The registry entry root.</param>
624 /// <param name="key">The registry entry key.</param>
625 /// <param name="name">The registry entry name.</param>
626 /// <param name="value">The registry entry value.</param>
627 /// <param name="componentId">The component which will control installation/uninstallation of the registry entry.</param>
628 public Identifier CreateRegistryRow(SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId) 422 public Identifier CreateRegistryRow(SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId)
629 { 423 {
630 return this.CreateRegistryRow(sourceLineNumbers, root, key, name, value, componentId, true); 424 return this.parseHelper.CreateRegistryRow(this.ActiveSection, sourceLineNumbers, root, key, name, value, componentId, true);
631 } 425 }
632 426
633 /// <summary> 427 /// <summary>
@@ -646,9 +440,7 @@ namespace WixToolset
646 // If this simple reference hasn't been added to the active section already, add it. 440 // If this simple reference hasn't been added to the active section already, add it.
647 if (this.activeSectionSimpleReferences.Add(id)) 441 if (this.activeSectionSimpleReferences.Add(id))
648 { 442 {
649 var wixSimpleReferenceRow = (WixSimpleReferenceTuple)this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixSimpleReference); 443 this.parseHelper.CreateSimpleReference(this.ActiveSection, sourceLineNumbers, tableName, primaryKeys);
650 wixSimpleReferenceRow.Table = tableName;
651 wixSimpleReferenceRow.PrimaryKeys = joinedKeys;
652 } 444 }
653 } 445 }
654 } 446 }
@@ -665,21 +457,7 @@ namespace WixToolset
665 { 457 {
666 if (!this.EncounteredError) 458 if (!this.EncounteredError)
667 { 459 {
668 if (null == parentId || ComplexReferenceParentType.Unknown == parentType) 460 this.parseHelper.CreateWixGroupRow(this.ActiveSection, sourceLineNumbers, parentType, parentId, childType, childId);
669 {
670 return;
671 }
672
673 if (null == childId)
674 {
675 throw new ArgumentNullException("childId");
676 }
677
678 var row = (WixGroupTuple)this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixGroup);
679 row.ParentId = parentId;
680 row.ParentType = parentType;
681 row.ChildId = childId;
682 row.ChildType = childType;
683 } 461 }
684 } 462 }
685 463
@@ -693,16 +471,7 @@ namespace WixToolset
693 { 471 {
694 if (!this.EncounteredError) 472 if (!this.EncounteredError)
695 { 473 {
696 var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixEnsureTable); 474 this.parseHelper.EnsureTable(this.ActiveSection, sourceLineNumbers, tableName);
697 row.Set(0, tableName);
698
699 // We don't add custom table definitions to the tableDefinitions collection,
700 // so if it's not in there, it better be a custom table. If the Id is just wrong,
701 // instead of a custom table, we get an unresolved reference at link time.
702 if (!this.creator.TryGetTupleDefinitionByName(tableName, out var ignored))
703 {
704 this.CreateSimpleReference(sourceLineNumbers, "WixCustomTable", tableName);
705 }
706 } 475 }
707 } 476 }
708 477
@@ -716,7 +485,7 @@ namespace WixToolset
716 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] 485 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
717 public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly) 486 public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly)
718 { 487 {
719 return Common.GetAttributeValue(sourceLineNumbers, attribute, emptyRule); 488 return this.parseHelper.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
720 } 489 }
721 490
722 /// <summary> 491 /// <summary>
@@ -800,7 +569,7 @@ namespace WixToolset
800 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] 569 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
801 public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum) 570 public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum)
802 { 571 {
803 return Common.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum); 572 return this.parseHelper.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum);
804 } 573 }
805 574
806 /// <summary> 575 /// <summary>
@@ -813,39 +582,7 @@ namespace WixToolset
813 /// <returns>The attribute's long value or a special value if an error occurred during conversion.</returns> 582 /// <returns>The attribute's long value or a special value if an error occurred during conversion.</returns>
814 public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum) 583 public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum)
815 { 584 {
816 Debug.Assert(minimum > CompilerConstants.LongNotSet && minimum > CompilerConstants.IllegalLong, "The legal values for this attribute collide with at least one sentinel used during parsing."); 585 return this.parseHelper.GetAttributeLongValue(sourceLineNumbers, attribute, minimum, maximum);
817
818 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
819
820 if (0 < value.Length)
821 {
822 try
823 {
824 long longValue = Convert.ToInt64(value, CultureInfo.InvariantCulture.NumberFormat);
825
826 if (CompilerConstants.LongNotSet == longValue || CompilerConstants.IllegalLong == longValue)
827 {
828 this.OnMessage(WixErrors.IntegralValueSentinelCollision(sourceLineNumbers, longValue));
829 }
830 else if (minimum > longValue || maximum < longValue)
831 {
832 this.OnMessage(WixErrors.IntegralValueOutOfRange(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, longValue, minimum, maximum));
833 longValue = CompilerConstants.IllegalLong;
834 }
835
836 return longValue;
837 }
838 catch (FormatException)
839 {
840 this.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
841 }
842 catch (OverflowException)
843 {
844 this.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
845 }
846 }
847
848 return CompilerConstants.IllegalLong;
849 } 586 }
850 587
851 /// <summary> 588 /// <summary>
@@ -956,73 +693,9 @@ namespace WixToolset
956 /// <param name="generatable">Determines whether the guid can be automatically generated.</param> 693 /// <param name="generatable">Determines whether the guid can be automatically generated.</param>
957 /// <param name="canBeEmpty">If true, no error is raised on empty value. If false, an error is raised.</param> 694 /// <param name="canBeEmpty">If true, no error is raised on empty value. If false, an error is raised.</param>
958 /// <returns>The attribute's guid value or a special value if an error occurred.</returns> 695 /// <returns>The attribute's guid value or a special value if an error occurred.</returns>
959 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
960 [SuppressMessage("Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation")]
961 public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false) 696 public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false)
962 { 697 {
963 if (null == attribute) 698 return this.parseHelper.GetAttributeGuidValue(sourceLineNumbers, attribute, generatable, canBeEmpty);
964 {
965 throw new ArgumentNullException("attribute");
966 }
967
968 EmptyRule emptyRule = canBeEmpty ? EmptyRule.CanBeEmpty : EmptyRule.CanBeWhitespaceOnly;
969 string value = this.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
970
971 if (String.IsNullOrEmpty(value) && canBeEmpty)
972 {
973 return String.Empty;
974 }
975 else if (!String.IsNullOrEmpty(value))
976 {
977 // If the value starts and ends with braces or parenthesis, accept that and strip them off.
978 if ((value.StartsWith("{", StringComparison.Ordinal) && value.EndsWith("}", StringComparison.Ordinal))
979 || (value.StartsWith("(", StringComparison.Ordinal) && value.EndsWith(")", StringComparison.Ordinal)))
980 {
981 value = value.Substring(1, value.Length - 2);
982 }
983
984 try
985 {
986 Guid guid;
987
988 if (generatable && "*".Equals(value, StringComparison.Ordinal))
989 {
990 return value;
991 }
992
993 if (CompilerCore.PutGuidHere.IsMatch(value))
994 {
995 this.OnMessage(WixErrors.ExampleGuid(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
996 return CompilerConstants.IllegalGuid;
997 }
998 else if (value.StartsWith("!(loc", StringComparison.Ordinal) || value.StartsWith("$(loc", StringComparison.Ordinal) || value.StartsWith("!(wix", StringComparison.Ordinal))
999 {
1000 return value;
1001 }
1002 else
1003 {
1004 guid = new Guid(value);
1005 }
1006
1007 string uppercaseGuid = guid.ToString().ToUpper(CultureInfo.InvariantCulture);
1008
1009 if (this.ShowPedanticMessages)
1010 {
1011 if (uppercaseGuid != value)
1012 {
1013 this.OnMessage(WixErrors.GuidContainsLowercaseLetters(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1014 }
1015 }
1016
1017 return String.Concat("{", uppercaseGuid, "}");
1018 }
1019 catch (FormatException)
1020 {
1021 this.OnMessage(WixErrors.IllegalGuidValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1022 }
1023 }
1024
1025 return CompilerConstants.IllegalGuid;
1026 } 699 }
1027 700
1028 /// <summary> 701 /// <summary>
@@ -1033,27 +706,7 @@ namespace WixToolset
1033 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns> 706 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns>
1034 public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute) 707 public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute)
1035 { 708 {
1036 string value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeEmpty); 709 return this.parseHelper.GetAttributeIdentifier(sourceLineNumbers, attribute);
1037 AccessModifier access = AccessModifier.Public;
1038
1039 Match match = CompilerCore.LegalIdentifierWithAccess.Match(value);
1040 if (!match.Success)
1041 {
1042 return null;
1043 }
1044 else if (match.Groups["access"].Success)
1045 {
1046 access = (AccessModifier)Enum.Parse(typeof(AccessModifier), match.Groups["access"].Value, true);
1047 }
1048
1049 value = match.Groups["id"].Value;
1050
1051 if (Common.IsIdentifier(value) && 72 < value.Length)
1052 {
1053 this.OnMessage(WixWarnings.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1054 }
1055
1056 return new Identifier(value, access);
1057 } 710 }
1058 711
1059 /// <summary> 712 /// <summary>
@@ -1064,7 +717,7 @@ namespace WixToolset
1064 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns> 717 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns>
1065 public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) 718 public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
1066 { 719 {
1067 return Common.GetAttributeIdentifierValue(sourceLineNumbers, attribute); 720 return this.parseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attribute);
1068 } 721 }
1069 722
1070 /// <summary> 723 /// <summary>
@@ -1075,23 +728,7 @@ namespace WixToolset
1075 /// <returns>The attribute's YesNoType value.</returns> 728 /// <returns>The attribute's YesNoType value.</returns>
1076 public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) 729 public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
1077 { 730 {
1078 string value = this.GetAttributeValue(sourceLineNumbers, attribute); 731 return this.parseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute);
1079
1080 YesNoType result = YesNoType.IllegalValue;
1081 if (value.Equals("yes") || value.Equals("true"))
1082 {
1083 result = YesNoType.Yes;
1084 }
1085 else if (value.Equals("no") || value.Equals("false"))
1086 {
1087 result = YesNoType.No;
1088 }
1089 else
1090 {
1091 this.OnMessage(WixErrors.IllegalYesNoValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1092 }
1093
1094 return result;
1095 } 732 }
1096 733
1097 /// <summary> 734 /// <summary>
@@ -1103,28 +740,7 @@ namespace WixToolset
1103 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] 740 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
1104 public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) 741 public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
1105 { 742 {
1106 string value = this.GetAttributeValue(sourceLineNumbers, attribute); 743 return this.parseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attribute);
1107
1108 if (0 < value.Length)
1109 {
1110 switch (Wix.Enums.ParseYesNoDefaultType(value))
1111 {
1112 case Wix.YesNoDefaultType.@default:
1113 return YesNoDefaultType.Default;
1114 case Wix.YesNoDefaultType.no:
1115 return YesNoDefaultType.No;
1116 case Wix.YesNoDefaultType.yes:
1117 return YesNoDefaultType.Yes;
1118 case Wix.YesNoDefaultType.NotSet:
1119 // Previous code never returned 'NotSet'!
1120 break;
1121 default:
1122 this.OnMessage(WixErrors.IllegalYesNoDefaultValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1123 break;
1124 }
1125 }
1126
1127 return YesNoDefaultType.IllegalValue;
1128 } 744 }
1129 745
1130 /// <summary> 746 /// <summary>
@@ -1202,41 +818,7 @@ namespace WixToolset
1202 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")] 818 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
1203 public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards = false, bool allowRelative = false) 819 public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards = false, bool allowRelative = false)
1204 { 820 {
1205 if (null == attribute) 821 return this.parseHelper.GetAttributeLongFilename(sourceLineNumbers, attribute, allowWildcards, allowRelative);
1206 {
1207 throw new ArgumentNullException("attribute");
1208 }
1209
1210 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
1211
1212 if (0 < value.Length)
1213 {
1214 if (!this.IsValidLongFilename(value, allowWildcards, allowRelative) && !this.IsValidLocIdentifier(value))
1215 {
1216 if (allowRelative)
1217 {
1218 this.OnMessage(WixErrors.IllegalRelativeLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1219 }
1220 else
1221 {
1222 this.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1223 }
1224 }
1225 else if (allowRelative)
1226 {
1227 string normalizedPath = value.Replace('\\', '/');
1228 if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../"))
1229 {
1230 this.OnMessage(WixErrors.PayloadMustBeRelativeToCache(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1231 }
1232 }
1233 else if (CompilerCore.IsAmbiguousFilename(value))
1234 {
1235 this.OnMessage(WixWarnings.AmbiguousFileOrDirectoryName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1236 }
1237 }
1238
1239 return value;
1240 } 822 }
1241 823
1242 /// <summary> 824 /// <summary>
@@ -1247,31 +829,7 @@ namespace WixToolset
1247 /// <returns>The attribute's version value.</returns> 829 /// <returns>The attribute's version value.</returns>
1248 public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute) 830 public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
1249 { 831 {
1250 string value = this.GetAttributeValue(sourceLineNumbers, attribute); 832 return this.parseHelper.GetAttributeVersionValue(sourceLineNumbers, attribute);
1251
1252 if (!String.IsNullOrEmpty(value))
1253 {
1254 try
1255 {
1256 return new Version(value).ToString();
1257 }
1258 catch (FormatException) // illegal integer in version
1259 {
1260 // Allow versions to contain binder variables.
1261 if (Common.ContainsValidBinderVariable(value))
1262 {
1263 return value;
1264 }
1265
1266 this.OnMessage(WixErrors.IllegalVersionValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1267 }
1268 catch (ArgumentException)
1269 {
1270 this.OnMessage(WixErrors.IllegalVersionValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1271 }
1272 }
1273
1274 return null;
1275 } 833 }
1276 834
1277 /// <summary> 835 /// <summary>
@@ -1453,7 +1011,7 @@ namespace WixToolset
1453 /// <returns>True if a property is found in the string.</returns> 1011 /// <returns>True if a property is found in the string.</returns>
1454 public bool ContainsProperty(string possibleProperty) 1012 public bool ContainsProperty(string possibleProperty)
1455 { 1013 {
1456 return Common.ContainsProperty(possibleProperty); 1014 return this.parseHelper.ContainsProperty(possibleProperty);
1457 } 1015 }
1458 1016
1459 /// <summary> 1017 /// <summary>
@@ -1465,8 +1023,7 @@ namespace WixToolset
1465 [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")] 1023 [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")]
1466 public Identifier CreateIdentifier(string prefix, params string[] args) 1024 public Identifier CreateIdentifier(string prefix, params string[] args)
1467 { 1025 {
1468 string id = Common.GenerateIdentifier(prefix, args); 1026 return this.parseHelper.CreateIdentifier(prefix, args);
1469 return new Identifier(id, AccessModifier.Private);
1470 } 1027 }
1471 1028
1472 /// <summary> 1029 /// <summary>
@@ -1476,8 +1033,7 @@ namespace WixToolset
1476 /// <returns></returns> 1033 /// <returns></returns>
1477 public Identifier CreateIdentifierFromFilename(string filename) 1034 public Identifier CreateIdentifierFromFilename(string filename)
1478 { 1035 {
1479 string id = Common.GetIdentifierFromName(filename); 1036 return this.parseHelper.CreateIdentifierFromFilename(filename);
1480 return new Identifier(id, AccessModifier.Private);
1481 } 1037 }
1482 1038
1483 /// <summary> 1039 /// <summary>
@@ -1488,22 +1044,7 @@ namespace WixToolset
1488 /// <param name="context">Extra information about the context in which this element is being parsed.</param> 1044 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
1489 public void ParseExtensionAttribute(XElement element, XAttribute attribute, IDictionary<string, string> context = null) 1045 public void ParseExtensionAttribute(XElement element, XAttribute attribute, IDictionary<string, string> context = null)
1490 { 1046 {
1491 // Ignore attributes defined by the W3C because we'll assume they are always right. 1047 this.parseHelper.ParseExtensionAttribute(this.extensions.Values, this.intermediate, this.ActiveSection, element, attribute, context);
1492 if ((String.IsNullOrEmpty(attribute.Name.NamespaceName) && attribute.Name.LocalName.Equals("xmlns", StringComparison.Ordinal)) ||
1493 attribute.Name.NamespaceName.StartsWith(CompilerCore.W3SchemaPrefix.NamespaceName, StringComparison.Ordinal))
1494 {
1495 return;
1496 }
1497
1498 if (this.TryFindExtension(attribute.Name.NamespaceName, out var extension))
1499 {
1500 extension.ParseAttribute(element, attribute, context);
1501 }
1502 else
1503 {
1504 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
1505 this.OnMessage(WixErrors.UnhandledExtensionAttribute(sourceLineNumbers, element.Name.LocalName, attribute.Name.LocalName, attribute.Name.NamespaceName));
1506 }
1507 } 1048 }
1508 1049
1509 /// <summary> 1050 /// <summary>
@@ -1514,16 +1055,7 @@ namespace WixToolset
1514 /// <param name="context">Extra information about the context in which this element is being parsed.</param> 1055 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
1515 public void ParseExtensionElement(XElement parentElement, XElement element, IDictionary<string, string> context = null) 1056 public void ParseExtensionElement(XElement parentElement, XElement element, IDictionary<string, string> context = null)
1516 { 1057 {
1517 if (this.TryFindExtension(element.Name.Namespace, out var extension)) 1058 this.parseHelper.ParseExtensionElement(this.extensions.Values, this.intermediate, this.ActiveSection, parentElement, element, context);
1518 {
1519 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement);
1520 extension.ParseElement(parentElement, element, context);
1521 }
1522 else
1523 {
1524 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
1525 this.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName));
1526 }
1527 } 1059 }
1528 1060
1529 /// <summary> 1061 /// <summary>
@@ -1532,17 +1064,7 @@ namespace WixToolset
1532 /// <param name="element">Element to parse children.</param> 1064 /// <param name="element">Element to parse children.</param>
1533 public void ParseForExtensionElements(XElement element) 1065 public void ParseForExtensionElements(XElement element)
1534 { 1066 {
1535 foreach (XElement child in element.Elements()) 1067 this.parseHelper.ParseForExtensionElements(this.extensions.Values, this.intermediate, this.ActiveSection, element);
1536 {
1537 if (element.Name.Namespace == child.Name.Namespace)
1538 {
1539 this.UnexpectedElement(element, child);
1540 }
1541 else
1542 {
1543 this.ParseExtensionElement(element, child);
1544 }
1545 }
1546 } 1068 }
1547 1069
1548 /// <summary> 1070 /// <summary>
@@ -1553,20 +1075,7 @@ namespace WixToolset
1553 /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param> 1075 /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param>
1554 public ComponentKeyPath ParsePossibleKeyPathExtensionElement(XElement parentElement, XElement element, IDictionary<string, string> context) 1076 public ComponentKeyPath ParsePossibleKeyPathExtensionElement(XElement parentElement, XElement element, IDictionary<string, string> context)
1555 { 1077 {
1556 ComponentKeyPath keyPath = null; 1078 return this.parseHelper.ParsePossibleKeyPathExtensionElement(this.extensions.Values, this.intermediate, this.ActiveSection, parentElement, element, context);
1557
1558 ICompilerExtension extension;
1559 if (this.TryFindExtension(element.Name.Namespace, out extension))
1560 {
1561 keyPath = extension.ParsePossibleKeyPathElement(parentElement, element, context);
1562 }
1563 else
1564 {
1565 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
1566 this.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName));
1567 }
1568
1569 return keyPath;
1570 } 1079 }
1571 1080
1572 /// <summary> 1081 /// <summary>
@@ -1576,8 +1085,7 @@ namespace WixToolset
1576 /// <param name="attribute">The unexpected attribute.</param> 1085 /// <param name="attribute">The unexpected attribute.</param>
1577 public void UnexpectedAttribute(XElement element, XAttribute attribute) 1086 public void UnexpectedAttribute(XElement element, XAttribute attribute)
1578 { 1087 {
1579 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); 1088 this.parseHelper.UnexpectedAttribute(element, attribute);
1580 Common.UnexpectedAttribute(sourceLineNumbers, attribute);
1581 } 1089 }
1582 1090
1583 /// <summary> 1091 /// <summary>
@@ -1587,9 +1095,7 @@ namespace WixToolset
1587 /// <param name="childElement">The unexpected child element.</param> 1095 /// <param name="childElement">The unexpected child element.</param>
1588 public void UnexpectedElement(XElement parentElement, XElement childElement) 1096 public void UnexpectedElement(XElement parentElement, XElement childElement)
1589 { 1097 {
1590 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(childElement); 1098 this.parseHelper.UnexpectedElement(parentElement, childElement);
1591
1592 this.OnMessage(WixErrors.UnexpectedElement(sourceLineNumbers, parentElement.Name.LocalName, childElement.Name.LocalName));
1593 } 1099 }
1594 1100
1595 /// <summary> 1101 /// <summary>
@@ -1667,30 +1173,6 @@ namespace WixToolset
1667 } 1173 }
1668 1174
1669 /// <summary> 1175 /// <summary>
1670 /// Creates a WiX complex reference in the active section.
1671 /// </summary>
1672 /// <param name="sourceLineNumbers">Source line information.</param>
1673 /// <param name="parentType">The parent type.</param>
1674 /// <param name="parentId">The parent id.</param>
1675 /// <param name="parentLanguage">The parent language.</param>
1676 /// <param name="childType">The child type.</param>
1677 /// <param name="childId">The child id.</param>
1678 /// <param name="isPrimary">Whether the child is primary.</param>
1679 internal void CreateWixComplexReferenceRow(SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary)
1680 {
1681 if (!this.EncounteredError)
1682 {
1683 var wixComplexReferenceRow = (WixComplexReferenceTuple)this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixComplexReference);
1684 wixComplexReferenceRow.Parent = parentId;
1685 wixComplexReferenceRow.ParentType = parentType;
1686 wixComplexReferenceRow.ParentLanguage = parentLanguage;
1687 wixComplexReferenceRow.Child = childId;
1688 wixComplexReferenceRow.ChildType = childType;
1689 wixComplexReferenceRow.IsPrimary = isPrimary;
1690 }
1691 }
1692
1693 /// <summary>
1694 /// Creates WixComplexReference and WixGroup rows in the active section. 1176 /// Creates WixComplexReference and WixGroup rows in the active section.
1695 /// </summary> 1177 /// </summary>
1696 /// <param name="sourceLineNumbers">Source line information.</param> 1178 /// <param name="sourceLineNumbers">Source line information.</param>
@@ -1702,8 +1184,7 @@ namespace WixToolset
1702 /// <param name="isPrimary">Whether the child is primary.</param> 1184 /// <param name="isPrimary">Whether the child is primary.</param>
1703 public void CreateComplexReference(SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary) 1185 public void CreateComplexReference(SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary)
1704 { 1186 {
1705 this.CreateWixComplexReferenceRow(sourceLineNumbers, parentType, parentId, parentLanguage, childType, childId, isPrimary); 1187 this.parseHelper.CreateComplexReference(this.ActiveSection, sourceLineNumbers, parentType, parentId, parentLanguage, childType, childId, isPrimary);
1706 this.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, childType, childId);
1707 } 1188 }
1708 1189
1709 /// <summary> 1190 /// <summary>
@@ -1719,56 +1200,57 @@ namespace WixToolset
1719 /// <returns>Identifier for the newly created row.</returns> 1200 /// <returns>Identifier for the newly created row.</returns>
1720 internal Identifier CreateDirectoryRow(SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null) 1201 internal Identifier CreateDirectoryRow(SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null)
1721 { 1202 {
1722 string defaultDir = null; 1203 //string defaultDir = null;
1723 1204
1724 if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false)) 1205 //if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false))
1725 { 1206 //{
1726 defaultDir = name; 1207 // defaultDir = name;
1727 } 1208 //}
1728 else 1209 //else
1729 { 1210 //{
1730 if (String.IsNullOrEmpty(shortName)) 1211 // if (String.IsNullOrEmpty(shortName))
1731 { 1212 // {
1732 shortName = this.CreateShortName(name, false, false, "Directory", parentId); 1213 // shortName = this.CreateShortName(name, false, false, "Directory", parentId);
1733 } 1214 // }
1734 1215
1735 defaultDir = String.Concat(shortName, "|", name); 1216 // defaultDir = String.Concat(shortName, "|", name);
1736 } 1217 //}
1737 1218
1738 if (!String.IsNullOrEmpty(sourceName)) 1219 //if (!String.IsNullOrEmpty(sourceName))
1739 { 1220 //{
1740 if (this.IsValidShortFilename(sourceName, false)) 1221 // if (this.IsValidShortFilename(sourceName, false))
1741 { 1222 // {
1742 defaultDir = String.Concat(defaultDir, ":", sourceName); 1223 // defaultDir = String.Concat(defaultDir, ":", sourceName);
1743 } 1224 // }
1744 else 1225 // else
1745 { 1226 // {
1746 if (String.IsNullOrEmpty(shortSourceName)) 1227 // if (String.IsNullOrEmpty(shortSourceName))
1747 { 1228 // {
1748 shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId); 1229 // shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId);
1749 } 1230 // }
1750 1231
1751 defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName); 1232 // defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName);
1752 } 1233 // }
1753 } 1234 //}
1754 1235
1755 // For anonymous directories, create the identifier. If this identifier already exists in the 1236 //// For anonymous directories, create the identifier. If this identifier already exists in the
1756 // active section, bail so we don't add duplicate anonymous directory rows (which are legal 1237 //// active section, bail so we don't add duplicate anonymous directory rows (which are legal
1757 // but bloat the intermediate and ultimately make the linker do "busy work"). 1238 //// but bloat the intermediate and ultimately make the linker do "busy work").
1758 if (null == id) 1239 //if (null == id)
1759 { 1240 //{
1760 id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); 1241 // id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName);
1761 1242
1762 if (!this.activeSectionInlinedDirectoryIds.Add(id.Id)) 1243 // if (!this.activeSectionInlinedDirectoryIds.Add(id.Id))
1763 { 1244 // {
1764 return id; 1245 // return id;
1765 } 1246 // }
1766 } 1247 //}
1767 1248
1768 var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.Directory, id); 1249 //var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.Directory, id);
1769 row.Set(1, parentId); 1250 //row.Set(1, parentId);
1770 row.Set(2, defaultDir); 1251 //row.Set(2, defaultDir);
1771 return id; 1252 //return id;
1253 return this.parseHelper.CreateDirectoryRow(this.ActiveSection, sourceLineNumbers, id, parentId, name, shortName, sourceName, shortSourceName, this.activeSectionInlinedDirectoryIds);
1772 } 1254 }
1773 1255
1774 /// <summary> 1256 /// <summary>
@@ -1780,65 +1262,7 @@ namespace WixToolset
1780 /// <returns>Inline directory syntax split into array of strings or null if the syntax did not parse.</returns> 1262 /// <returns>Inline directory syntax split into array of strings or null if the syntax did not parse.</returns>
1781 internal string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false) 1263 internal string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false)
1782 { 1264 {
1783 string[] result = null; 1265 return this.parseHelper.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, resultUsedToCreateReference);
1784 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
1785
1786 if (!String.IsNullOrEmpty(value))
1787 {
1788 int pathStartsAt = 0;
1789 result = value.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
1790 if (result[0].EndsWith(":", StringComparison.Ordinal))
1791 {
1792 string id = result[0].TrimEnd(':');
1793 if (1 == result.Length)
1794 {
1795 this.OnMessage(WixErrors.InlineDirectorySyntaxRequiresPath(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id));
1796 return null;
1797 }
1798 else if (!this.IsValidIdentifier(id))
1799 {
1800 this.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id));
1801 return null;
1802 }
1803
1804 pathStartsAt = 1;
1805 }
1806 else if (resultUsedToCreateReference && 1 == result.Length)
1807 {
1808 if (value.EndsWith("\\"))
1809 {
1810 if (!this.IsValidLongFilename(result[0]))
1811 {
1812 this.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0]));
1813 return null;
1814 }
1815 }
1816 else if (!this.IsValidIdentifier(result[0]))
1817 {
1818 this.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0]));
1819 return null;
1820 }
1821
1822 return result; // return early to avoid additional checks below.
1823 }
1824
1825 // Check each part of the relative path to ensure that it is a valid directory name.
1826 for (int i = pathStartsAt; i < result.Length; ++i)
1827 {
1828 if (!this.IsValidLongFilename(result[i]))
1829 {
1830 this.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[i]));
1831 return null;
1832 }
1833 }
1834
1835 if (1 < result.Length && !value.EndsWith("\\"))
1836 {
1837 this.OnMessage(WixWarnings.BackslashTerminateInlineDirectorySyntax(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
1838 }
1839 }
1840
1841 return result;
1842 } 1266 }
1843 1267
1844 /// <summary> 1268 /// <summary>
diff --git a/src/WixToolset.Core/Converter.cs b/src/WixToolset.Core/Converter.cs
index 6ae2f984..c0297758 100644
--- a/src/WixToolset.Core/Converter.cs
+++ b/src/WixToolset.Core/Converter.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
diff --git a/src/WixToolset.Core/Extensibility/HeatExtension.cs b/src/WixToolset.Core/Extensibility/HeatExtension.cs
index 48e1a93b..2b4a6823 100644
--- a/src/WixToolset.Core/Extensibility/HeatExtension.cs
+++ b/src/WixToolset.Core/Extensibility/HeatExtension.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset.Extensibility 3namespace WixToolset.Core.Extensibility
4{ 4{
5 using System; 5 using System;
6 using System.IO; 6 using System.IO;
diff --git a/src/WixToolset.Core/Extensibility/IHeatCore.cs b/src/WixToolset.Core/Extensibility/IHeatCore.cs
index dbfc8929..031c8132 100644
--- a/src/WixToolset.Core/Extensibility/IHeatCore.cs
+++ b/src/WixToolset.Core/Extensibility/IHeatCore.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset.Extensibility 3namespace WixToolset.Core.Extensibility
4{ 4{
5 using WixToolset.Data; 5 using WixToolset.Data;
6 6
diff --git a/src/WixToolset.Core/Extensibility/MutatorExtension.cs b/src/WixToolset.Core/Extensibility/MutatorExtension.cs
index 9de64180..f95b5b11 100644
--- a/src/WixToolset.Core/Extensibility/MutatorExtension.cs
+++ b/src/WixToolset.Core/Extensibility/MutatorExtension.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset.Extensibility 3namespace WixToolset.Core.Extensibility
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
diff --git a/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
new file mode 100644
index 00000000..87ad0da8
--- /dev/null
+++ b/src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
@@ -0,0 +1,824 @@
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.IO;
10 using System.Security.Cryptography;
11 using System.Text;
12 using System.Text.RegularExpressions;
13 using System.Xml.Linq;
14 using WixToolset.Data;
15 using WixToolset.Data.Tuples;
16 using WixToolset.Extensibility;
17 using WixToolset.Extensibility.Services;
18
19 internal class ParseHelper : IParseHelper
20 {
21 private const string LegalLongFilenameCharacters = @"[^\\\?|><:/\*""]"; // opposite of illegal above.
22 private static readonly Regex LegalLongFilename = new Regex(String.Concat("^", LegalLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled);
23
24 private const string LegalRelativeLongFilenameCharacters = @"[^\?|><:/\*""]"; // (like legal long, but we allow '\') illegal: ? | > < : / * "
25 private static readonly Regex LegalRelativeLongFilename = new Regex(String.Concat("^", LegalRelativeLongFilenameCharacters, @"{1,259}$"), RegexOptions.Compiled);
26
27 private const string LegalWildcardLongFilenameCharacters = @"[^\\|><:/""]"; // illegal: \ | > < : / "
28 private static readonly Regex LegalWildcardLongFilename = new Regex(String.Concat("^", LegalWildcardLongFilenameCharacters, @"{1,259}$"));
29
30 private static readonly Regex LegalIdentifierWithAccess = new Regex(@"^((?<access>public|internal|protected|private)\s+)?(?<id>[_A-Za-z][0-9A-Za-z_\.]*)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
31
32 private static readonly Regex PutGuidHere = new Regex(@"PUT\-GUID\-(?:\d+\-)?HERE", RegexOptions.Singleline);
33
34 public ParseHelper(IServiceProvider serviceProvider)
35 {
36 this.ServiceProvider = serviceProvider;
37 }
38
39 private IServiceProvider ServiceProvider { get; }
40
41 private ITupleDefinitionCreator Creator { get; set; }
42
43 public bool ContainsProperty(string possibleProperty)
44 {
45 return Common.ContainsProperty(possibleProperty);
46 }
47
48 public void CreateComplexReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary)
49 {
50 var wixComplexReferenceRow = (WixComplexReferenceTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixComplexReference);
51 wixComplexReferenceRow.Parent = parentId;
52 wixComplexReferenceRow.ParentType = parentType;
53 wixComplexReferenceRow.ParentLanguage = parentLanguage;
54 wixComplexReferenceRow.Child = childId;
55 wixComplexReferenceRow.ChildType = childType;
56 wixComplexReferenceRow.IsPrimary = isPrimary;
57
58 this.CreateWixGroupRow(section, sourceLineNumbers, parentType, parentId, childType, childId);
59 }
60
61 public Identifier CreateDirectoryRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null, ISet<string> sectionInlinedDirectoryIds = null)
62 {
63 string defaultDir = null;
64
65 if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false))
66 {
67 defaultDir = name;
68 }
69 else
70 {
71 if (String.IsNullOrEmpty(shortName))
72 {
73 shortName = this.CreateShortName(name, false, false, "Directory", parentId);
74 }
75
76 defaultDir = String.Concat(shortName, "|", name);
77 }
78
79 if (!String.IsNullOrEmpty(sourceName))
80 {
81 if (this.IsValidShortFilename(sourceName, false))
82 {
83 defaultDir = String.Concat(defaultDir, ":", sourceName);
84 }
85 else
86 {
87 if (String.IsNullOrEmpty(shortSourceName))
88 {
89 shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId);
90 }
91
92 defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName);
93 }
94 }
95
96 // For anonymous directories, create the identifier. If this identifier already exists in the
97 // active section, bail so we don't add duplicate anonymous directory rows (which are legal
98 // but bloat the intermediate and ultimately make the linker do "busy work").
99 if (null == id)
100 {
101 id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName);
102
103 if (!sectionInlinedDirectoryIds.Add(id.Id))
104 {
105 return id;
106 }
107 }
108
109 var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.Directory, id);
110 row.Set(1, parentId);
111 row.Set(2, defaultDir);
112 return id;
113 }
114
115 public string CreateDirectoryReferenceFromInlineSyntax(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId)
116 {
117 string id = null;
118 string[] inlineSyntax = this.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, true);
119
120 if (null != inlineSyntax)
121 {
122 // Special case the single entry in the inline syntax since it is the most common case
123 // and needs no extra processing. It's just a reference to an existing directory.
124 if (1 == inlineSyntax.Length)
125 {
126 id = inlineSyntax[0];
127 this.CreateSimpleReference(section, sourceLineNumbers, "Directory", id);
128 }
129 else // start creating rows for the entries in the inline syntax
130 {
131 id = parentId;
132
133 int pathStartsAt = 0;
134 if (inlineSyntax[0].EndsWith(":"))
135 {
136 // TODO: should overriding the parent identifier with a specific id be an error or a warning or just let it slide?
137 //if (null != parentId)
138 //{
139 // this.core.OnMessage(WixErrors.Xxx(sourceLineNumbers));
140 //}
141
142 id = inlineSyntax[0].TrimEnd(':');
143 this.CreateSimpleReference(section, sourceLineNumbers, "Directory", id);
144
145 pathStartsAt = 1;
146 }
147
148 for (int i = pathStartsAt; i < inlineSyntax.Length; ++i)
149 {
150 Identifier inlineId = this.CreateDirectoryRow(section, sourceLineNumbers, null, id, inlineSyntax[i]);
151 id = inlineId.Id;
152 }
153 }
154 }
155
156 return id;
157 }
158
159 public string CreateGuid(Guid namespaceGuid, string value)
160 {
161 return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant();
162 }
163
164 public Identifier CreateIdentifier(string prefix, params string[] args)
165 {
166 var id = Common.GenerateIdentifier(prefix, args);
167 return new Identifier(id, AccessModifier.Private);
168 }
169
170 public Identifier CreateIdentifierFromFilename(string filename)
171 {
172 var id = Common.GetIdentifierFromName(filename);
173 return new Identifier(id, AccessModifier.Private);
174 }
175
176 public Identifier CreateRegistryRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId, bool escapeLeadingHash)
177 {
178 Identifier id = null;
179
180 if (-1 > root || 3 < root)
181 {
182 throw new ArgumentOutOfRangeException("root");
183 }
184
185 if (null == key)
186 {
187 throw new ArgumentNullException("key");
188 }
189
190 if (null == componentId)
191 {
192 throw new ArgumentNullException("componentId");
193 }
194
195 // Escape the leading '#' character for string registry values.
196 if (escapeLeadingHash && null != value && value.StartsWith("#", StringComparison.Ordinal))
197 {
198 value = String.Concat("#", value);
199 }
200
201 id = this.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), key.ToLowerInvariant(), (null != name ? name.ToLowerInvariant() : name));
202
203 var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.Registry, id);
204 row.Set(1, root);
205 row.Set(2, key);
206 row.Set(3, name);
207 row.Set(4, value);
208 row.Set(5, componentId);
209
210 return id;
211 }
212
213 public void CreateSimpleReference(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName, params string[] primaryKeys)
214 {
215 var joinedKeys = String.Join("/", primaryKeys);
216 var id = String.Concat(tableName, ":", joinedKeys);
217
218 var wixSimpleReferenceRow = (WixSimpleReferenceTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixSimpleReference);
219 wixSimpleReferenceRow.Table = tableName;
220 wixSimpleReferenceRow.PrimaryKeys = joinedKeys;
221 }
222
223 public void CreateWixGroupRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType childType, string childId)
224 {
225 if (null == parentId || ComplexReferenceParentType.Unknown == parentType)
226 {
227 return;
228 }
229
230 if (null == childId)
231 {
232 throw new ArgumentNullException("childId");
233 }
234
235 var row = (WixGroupTuple)this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixGroup);
236 row.ParentId = parentId;
237 row.ParentType = parentType;
238 row.ChildId = childId;
239 row.ChildType = childType;
240 }
241
242 public IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName, Identifier identifier = null)
243 {
244 if (this.Creator == null)
245 {
246 this.CreateTupleDefinitionCreator();
247 }
248
249 if (!this.Creator.TryGetTupleDefinitionByName(tableName, out var tupleDefinition))
250 {
251 throw new ArgumentException(nameof(tableName));
252 }
253
254 return CreateRow(section, sourceLineNumbers, tupleDefinition, identifier);
255 }
256
257 public IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, TupleDefinitionType tupleType, Identifier identifier = null)
258 {
259 var tupleDefinition = TupleDefinitions.ByType(tupleType);
260
261 return CreateRow(section, sourceLineNumbers, tupleDefinition, identifier);
262 }
263
264 public string CreateShortName(string longName, bool keepExtension, bool allowWildcards, params string[] args)
265 {
266 // canonicalize the long name if its not a localization identifier (they are case-sensitive)
267 if (!this.IsValidLocIdentifier(longName))
268 {
269 longName = longName.ToLowerInvariant();
270 }
271
272 // collect all the data
273 List<string> strings = new List<string>(1 + args.Length);
274 strings.Add(longName);
275 strings.AddRange(args);
276
277 // prepare for hashing
278 string stringData = String.Join("|", strings);
279 byte[] data = Encoding.UTF8.GetBytes(stringData);
280
281 // hash the data
282 byte[] hash;
283 using (var sha1 = new SHA1CryptoServiceProvider())
284 {
285 hash = sha1.ComputeHash(data);
286 }
287
288 // generate the short file/directory name without an extension
289 StringBuilder shortName = new StringBuilder(Convert.ToBase64String(hash));
290 shortName.Remove(8, shortName.Length - 8).Replace('+', '-').Replace('/', '_');
291
292 if (keepExtension)
293 {
294 string extension = Path.GetExtension(longName);
295
296 if (4 < extension.Length)
297 {
298 extension = extension.Substring(0, 4);
299 }
300
301 shortName.Append(extension);
302
303 // check the generated short name to ensure its still legal (the extension may not be legal)
304 if (!this.IsValidShortFilename(shortName.ToString(), allowWildcards))
305 {
306 // remove the extension (by truncating the generated file name back to the generated characters)
307 shortName.Length -= extension.Length;
308 }
309 }
310
311 return shortName.ToString().ToLowerInvariant();
312 }
313
314 public void EnsureTable(IntermediateSection section, SourceLineNumber sourceLineNumbers, string tableName)
315 {
316 var row = this.CreateRow(section, sourceLineNumbers, TupleDefinitionType.WixEnsureTable);
317 row.Set(0, tableName);
318
319 if (this.Creator == null)
320 {
321 this.CreateTupleDefinitionCreator();
322 }
323
324 // We don't add custom table definitions to the tableDefinitions collection,
325 // so if it's not in there, it better be a custom table. If the Id is just wrong,
326 // instead of a custom table, we get an unresolved reference at link time.
327 if (!this.Creator.TryGetTupleDefinitionByName(tableName, out var ignored))
328 {
329 this.CreateSimpleReference(section, sourceLineNumbers, "WixCustomTable", tableName);
330 }
331 }
332
333 public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false)
334 {
335 if (null == attribute)
336 {
337 throw new ArgumentNullException("attribute");
338 }
339
340 EmptyRule emptyRule = canBeEmpty ? EmptyRule.CanBeEmpty : EmptyRule.CanBeWhitespaceOnly;
341 string value = this.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
342
343 if (String.IsNullOrEmpty(value) && canBeEmpty)
344 {
345 return String.Empty;
346 }
347 else if (!String.IsNullOrEmpty(value))
348 {
349 // If the value starts and ends with braces or parenthesis, accept that and strip them off.
350 if ((value.StartsWith("{", StringComparison.Ordinal) && value.EndsWith("}", StringComparison.Ordinal))
351 || (value.StartsWith("(", StringComparison.Ordinal) && value.EndsWith(")", StringComparison.Ordinal)))
352 {
353 value = value.Substring(1, value.Length - 2);
354 }
355
356 if (generatable && "*".Equals(value, StringComparison.Ordinal))
357 {
358 return value;
359 }
360
361 if (ParseHelper.PutGuidHere.IsMatch(value))
362 {
363 Messaging.Instance.OnMessage(WixErrors.ExampleGuid(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
364 return CompilerConstants.IllegalGuid;
365 }
366 else if (value.StartsWith("!(loc", StringComparison.Ordinal) || value.StartsWith("$(loc", StringComparison.Ordinal) || value.StartsWith("!(wix", StringComparison.Ordinal))
367 {
368 return value;
369 }
370 else if (Guid.TryParse(value, out var guid))
371 {
372 var uppercaseGuid = guid.ToString().ToUpperInvariant();
373
374 // TODO: This used to be a pedantic error, what should it be now?
375 //if (uppercaseGuid != value)
376 //{
377 // Messaging.Instance.OnMessage(WixErrors.GuidContainsLowercaseLetters(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
378 //}
379
380 return String.Concat("{", uppercaseGuid, "}");
381 }
382 else
383 {
384 Messaging.Instance.OnMessage(WixErrors.IllegalGuidValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
385 }
386 }
387
388 return CompilerConstants.IllegalGuid;
389 }
390
391 public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute)
392 {
393 var access = AccessModifier.Public;
394 var value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeEmpty);
395
396 var match = ParseHelper.LegalIdentifierWithAccess.Match(value);
397 if (!match.Success)
398 {
399 return null;
400 }
401 else if (match.Groups["access"].Success)
402 {
403 access = (AccessModifier)Enum.Parse(typeof(AccessModifier), match.Groups["access"].Value, true);
404 }
405
406 value = match.Groups["id"].Value;
407
408 if (Common.IsIdentifier(value) && 72 < value.Length)
409 {
410 Messaging.Instance.OnMessage(WixWarnings.IdentifierTooLong(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
411 }
412
413 return new Identifier(value, access);
414 }
415
416 public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
417 {
418 return Common.GetAttributeIdentifierValue(sourceLineNumbers, attribute);
419 }
420
421 public string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false)
422 {
423 string[] result = null;
424 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
425
426 if (!String.IsNullOrEmpty(value))
427 {
428 int pathStartsAt = 0;
429 result = value.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
430 if (result[0].EndsWith(":", StringComparison.Ordinal))
431 {
432 string id = result[0].TrimEnd(':');
433 if (1 == result.Length)
434 {
435 Messaging.Instance.OnMessage(WixErrors.InlineDirectorySyntaxRequiresPath(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id));
436 return null;
437 }
438 else if (!this.IsValidIdentifier(id))
439 {
440 Messaging.Instance.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, id));
441 return null;
442 }
443
444 pathStartsAt = 1;
445 }
446 else if (resultUsedToCreateReference && 1 == result.Length)
447 {
448 if (value.EndsWith("\\"))
449 {
450 if (!this.IsValidLongFilename(result[0], false, false))
451 {
452 Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0]));
453 return null;
454 }
455 }
456 else if (!this.IsValidIdentifier(result[0]))
457 {
458 Messaging.Instance.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[0]));
459 return null;
460 }
461
462 return result; // return early to avoid additional checks below.
463 }
464
465 // Check each part of the relative path to ensure that it is a valid directory name.
466 for (int i = pathStartsAt; i < result.Length; ++i)
467 {
468 if (!this.IsValidLongFilename(result[i], false, false))
469 {
470 Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value, result[i]));
471 return null;
472 }
473 }
474
475 if (1 < result.Length && !value.EndsWith("\\"))
476 {
477 Messaging.Instance.OnMessage(WixWarnings.BackslashTerminateInlineDirectorySyntax(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
478 }
479 }
480
481 return result;
482 }
483
484 public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum)
485 {
486 return Common.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum);
487 }
488
489 public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards, bool allowRelative)
490 {
491 if (null == attribute)
492 {
493 throw new ArgumentNullException("attribute");
494 }
495
496 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
497
498 if (0 < value.Length)
499 {
500 if (!this.IsValidLongFilename(value, allowWildcards, allowRelative) && !this.IsValidLocIdentifier(value))
501 {
502 if (allowRelative)
503 {
504 Messaging.Instance.OnMessage(WixErrors.IllegalRelativeLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
505 }
506 else
507 {
508 Messaging.Instance.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
509 }
510 }
511 else if (allowRelative)
512 {
513 string normalizedPath = value.Replace('\\', '/');
514 if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../"))
515 {
516 Messaging.Instance.OnMessage(WixErrors.PayloadMustBeRelativeToCache(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
517 }
518 }
519 else if (CompilerCore.IsAmbiguousFilename(value))
520 {
521 Messaging.Instance.OnMessage(WixWarnings.AmbiguousFileOrDirectoryName(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
522 }
523 }
524
525 return value;
526 }
527
528 public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum)
529 {
530 Debug.Assert(minimum > CompilerConstants.LongNotSet && minimum > CompilerConstants.IllegalLong, "The legal values for this attribute collide with at least one sentinel used during parsing.");
531
532 string value = this.GetAttributeValue(sourceLineNumbers, attribute);
533
534 if (0 < value.Length)
535 {
536 try
537 {
538 long longValue = Convert.ToInt64(value, CultureInfo.InvariantCulture.NumberFormat);
539
540 if (CompilerConstants.LongNotSet == longValue || CompilerConstants.IllegalLong == longValue)
541 {
542 Messaging.Instance.OnMessage(WixErrors.IntegralValueSentinelCollision(sourceLineNumbers, longValue));
543 }
544 else if (minimum > longValue || maximum < longValue)
545 {
546 Messaging.Instance.OnMessage(WixErrors.IntegralValueOutOfRange(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, longValue, minimum, maximum));
547 longValue = CompilerConstants.IllegalLong;
548 }
549
550 return longValue;
551 }
552 catch (FormatException)
553 {
554 Messaging.Instance.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
555 }
556 catch (OverflowException)
557 {
558 Messaging.Instance.OnMessage(WixErrors.IllegalLongValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
559 }
560 }
561
562 return CompilerConstants.IllegalLong;
563 }
564
565 public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly)
566 {
567 return Common.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
568 }
569
570 public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
571 {
572 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
573
574 if (!String.IsNullOrEmpty(value))
575 {
576 if (Version.TryParse(value, out var version))
577 {
578 return version.ToString();
579 }
580
581 // Allow versions to contain binder variables.
582 if (Common.ContainsValidBinderVariable(value))
583 {
584 return value;
585 }
586
587 Messaging.Instance.OnMessage(WixErrors.IllegalVersionValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
588 }
589
590 return null;
591 }
592
593 public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
594 {
595 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
596
597 switch (value)
598 {
599 case "yes":
600 case "true":
601 return YesNoDefaultType.Yes;
602
603 case "no":
604 case "false":
605 return YesNoDefaultType.No;
606
607 case "default":
608 return YesNoDefaultType.Default;
609
610 default:
611 Messaging.Instance.OnMessage(WixErrors.IllegalYesNoDefaultValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
612 return YesNoDefaultType.IllegalValue;
613 }
614 }
615
616 public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
617 {
618 var value = this.GetAttributeValue(sourceLineNumbers, attribute);
619
620 switch (value)
621 {
622 case "yes":
623 case "true":
624 return YesNoType.Yes;
625
626 case "no":
627 case "false":
628 return YesNoType.No;
629
630 default:
631 Messaging.Instance.OnMessage(WixErrors.IllegalYesNoValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
632 return YesNoType.IllegalValue;
633 }
634 }
635
636 public string GetConditionInnerText(XElement element)
637 {
638 var value = Common.GetInnerText(element)?.Trim().Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ');
639
640 // Return null for a non-existant condition.
641 return String.IsNullOrEmpty(value) ? null : value;
642 }
643
644 public string GetTrimmedInnerText(XElement element)
645 {
646 var value = Common.GetInnerText(element);
647 return value?.Trim();
648 }
649
650 public bool IsValidIdentifier(string value)
651 {
652 return Common.IsIdentifier(value);
653 }
654
655 public bool IsValidLocIdentifier(string identifier)
656 {
657 if (String.IsNullOrEmpty(identifier))
658 {
659 return false;
660 }
661
662 var match = Common.WixVariableRegex.Match(identifier);
663
664 return (match.Success && "loc" == match.Groups["namespace"].Value && 0 == match.Index && identifier.Length == match.Length);
665 }
666
667 public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
668 {
669 if (String.IsNullOrEmpty(filename))
670 {
671 return false;
672 }
673
674 // Check for a non-period character (all periods is not legal)
675 bool nonPeriodFound = false;
676 foreach (char character in filename)
677 {
678 if ('.' != character)
679 {
680 nonPeriodFound = true;
681 break;
682 }
683 }
684
685 if (allowWildcards)
686 {
687 return (nonPeriodFound && ParseHelper.LegalWildcardLongFilename.IsMatch(filename));
688 }
689 else if (allowRelative)
690 {
691 return (nonPeriodFound && ParseHelper.LegalRelativeLongFilename.IsMatch(filename));
692 }
693 else
694 {
695 return (nonPeriodFound && ParseHelper.LegalLongFilename.IsMatch(filename));
696 }
697 }
698
699 public bool IsValidShortFilename(string filename, bool allowWildcards = false)
700 {
701 return Common.IsValidShortFilename(filename, allowWildcards);
702 }
703
704 public void ParseExtensionAttribute(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element, XAttribute attribute, IDictionary<string, string> context = null)
705 {
706 // Ignore attributes defined by the W3C because we'll assume they are always right.
707 if ((String.IsNullOrEmpty(attribute.Name.NamespaceName) && attribute.Name.LocalName.Equals("xmlns", StringComparison.Ordinal)) ||
708 attribute.Name.NamespaceName.StartsWith(CompilerCore.W3SchemaPrefix.NamespaceName, StringComparison.Ordinal))
709 {
710 return;
711 }
712
713 if (ParseHelper.TryFindExtension(extensions, attribute.Name.NamespaceName, out var extension))
714 {
715 extension.ParseAttribute(intermediate, section, element, attribute, context);
716 }
717 else
718 {
719 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
720 Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionAttribute(sourceLineNumbers, element.Name.LocalName, attribute.Name.LocalName, attribute.Name.NamespaceName));
721 }
722 }
723
724 public void ParseExtensionElement(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context = null)
725 {
726 if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension))
727 {
728 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement);
729 extension.ParseElement(intermediate, section, parentElement, element, context);
730 }
731 else
732 {
733 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
734 Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName));
735 }
736 }
737
738 public ComponentKeyPath ParsePossibleKeyPathExtensionElement(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
739 {
740 ComponentKeyPath keyPath = null;
741
742 if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension))
743 {
744 keyPath = extension.ParsePossibleKeyPathElement(intermediate, section, parentElement, element, context);
745 }
746 else
747 {
748 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
749 Messaging.Instance.OnMessage(WixErrors.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName));
750 }
751
752 return keyPath;
753 }
754
755 public void ParseForExtensionElements(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element)
756 {
757 foreach (XElement child in element.Elements())
758 {
759 if (element.Name.Namespace == child.Name.Namespace)
760 {
761 this.UnexpectedElement(element, child);
762 }
763 else
764 {
765 this.ParseExtensionElement(extensions, intermediate, section, element, child);
766 }
767 }
768 }
769
770 public void UnexpectedAttribute(XElement element, XAttribute attribute)
771 {
772 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
773 Common.UnexpectedAttribute(sourceLineNumbers, attribute);
774 }
775
776 public void UnexpectedElement(XElement parentElement, XElement childElement)
777 {
778 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(childElement);
779 Messaging.Instance.OnMessage(WixErrors.UnexpectedElement(sourceLineNumbers, parentElement.Name.LocalName, childElement.Name.LocalName));
780 }
781
782 private void CreateTupleDefinitionCreator()
783 {
784 this.Creator = (ITupleDefinitionCreator)this.ServiceProvider.GetService(typeof(ITupleDefinitionCreator));
785 }
786
787 private static IntermediateTuple CreateRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, IntermediateTupleDefinition tupleDefinition, Identifier identifier)
788 {
789 var row = tupleDefinition.CreateTuple(sourceLineNumbers, identifier);
790
791 if (null != identifier)
792 {
793 if (row.Definition.FieldDefinitions[0].Type == IntermediateFieldType.Number)
794 {
795 row.Set(0, Convert.ToInt32(identifier.Id));
796 }
797 else
798 {
799 row.Set(0, identifier.Id);
800 }
801 }
802
803 section.Tuples.Add(row);
804
805 return row;
806 }
807
808 private static bool TryFindExtension(IEnumerable<ICompilerExtension> extensions, XNamespace ns, out ICompilerExtension extension)
809 {
810 extension = null;
811
812 foreach (var ext in extensions)
813 {
814 if (ext.Namespace == ns)
815 {
816 extension = ext;
817 break;
818 }
819 }
820
821 return extension != null;
822 }
823 }
824}
diff --git a/src/WixToolset.Core/TupleDefinitionCreator.cs b/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs
index 8c9b9d29..4075def8 100644
--- a/src/WixToolset.Core/TupleDefinitionCreator.cs
+++ b/src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset.Core 3namespace WixToolset.Core.ExtensibilityServices
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
diff --git a/src/WixToolset.Core/HarvesterCore.cs b/src/WixToolset.Core/HarvesterCore.cs
index 66a693f2..87e3c33f 100644
--- a/src/WixToolset.Core/HarvesterCore.cs
+++ b/src/WixToolset.Core/HarvesterCore.cs
@@ -1,12 +1,11 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Diagnostics.CodeAnalysis; 6 using System.Diagnostics.CodeAnalysis;
7 using System.IO; 7 using System.IO;
8 using WixToolset.Data; 8 using WixToolset.Data;
9 using Wix = WixToolset.Data.Serialize;
10 9
11 /// <summary> 10 /// <summary>
12 /// The WiX Toolset harvester core. 11 /// The WiX Toolset harvester core.
diff --git a/src/WixToolset.Core/HeatCore.cs b/src/WixToolset.Core/HeatCore.cs
index 01233c40..2384a1ed 100644
--- a/src/WixToolset.Core/HeatCore.cs
+++ b/src/WixToolset.Core/HeatCore.cs
@@ -1,9 +1,9 @@
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. 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 2
3namespace WixToolset.Tools 3namespace WixToolset.Core
4{ 4{
5 using WixToolset.Core.Extensibility;
5 using WixToolset.Data; 6 using WixToolset.Data;
6 using WixToolset.Extensibility;
7 7
8 /// <summary> 8 /// <summary>
9 /// The WiX Toolset Harvester application core. 9 /// The WiX Toolset Harvester application core.
diff --git a/src/WixToolset.Core/Link/WixGroupingOrdering.cs b/src/WixToolset.Core/Link/WixGroupingOrdering.cs
index 4dd1596c..f15ebc75 100644
--- a/src/WixToolset.Core/Link/WixGroupingOrdering.cs
+++ b/src/WixToolset.Core/Link/WixGroupingOrdering.cs
@@ -1,16 +1,14 @@
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. 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 2
3namespace WixToolset.Link 3namespace WixToolset.Core.Link
4{ 4{
5 using System; 5 using System;
6 using System.Collections;
7 using System.Collections.ObjectModel; 6 using System.Collections.ObjectModel;
8 using System.Collections.Generic; 7 using System.Collections.Generic;
9 using System.Diagnostics; 8 using System.Diagnostics;
10 using System.Globalization; 9 using System.Globalization;
11 using System.Linq; 10 using System.Linq;
12 using System.Text; 11 using System.Text;
13 using WixToolset.Extensibility;
14 using WixToolset.Data; 12 using WixToolset.Data;
15 using WixToolset.Data.Tuples; 13 using WixToolset.Data.Tuples;
16 14
diff --git a/src/WixToolset.Core/Localizer.cs b/src/WixToolset.Core/Localizer.cs
index a19c32fb..8265c026 100644
--- a/src/WixToolset.Core/Localizer.cs
+++ b/src/WixToolset.Core/Localizer.cs
@@ -1,6 +1,6 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
diff --git a/src/WixToolset.Core/Mutator.cs b/src/WixToolset.Core/Mutator.cs
index d37815f4..f935b570 100644
--- a/src/WixToolset.Core/Mutator.cs
+++ b/src/WixToolset.Core/Mutator.cs
@@ -1,10 +1,10 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
7 using WixToolset.Extensibility; 7 using WixToolset.Core.Extensibility;
8 using Wix = WixToolset.Data.Serialize; 8 using Wix = WixToolset.Data.Serialize;
9 9
10 /// <summary> 10 /// <summary>
diff --git a/src/WixToolset.Core/Preprocessor.cs b/src/WixToolset.Core/Preprocessor.cs
index 85b3dab8..23f568a8 100644
--- a/src/WixToolset.Core/Preprocessor.cs
+++ b/src/WixToolset.Core/Preprocessor.cs
@@ -1,9 +1,8 @@
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. 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 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections;
7 using System.Collections.Generic; 6 using System.Collections.Generic;
8 using System.Diagnostics.CodeAnalysis; 7 using System.Diagnostics.CodeAnalysis;
9 using System.Globalization; 8 using System.Globalization;
diff --git a/src/WixToolset.Core/WixToolsetServiceProvider.cs b/src/WixToolset.Core/WixToolsetServiceProvider.cs
index dd49e7ed..8693461b 100644
--- a/src/WixToolset.Core/WixToolsetServiceProvider.cs
+++ b/src/WixToolset.Core/WixToolsetServiceProvider.cs
@@ -3,6 +3,7 @@
3namespace WixToolset.Core 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using WixToolset.Core.ExtensibilityServices;
6 using WixToolset.Data; 7 using WixToolset.Data;
7 using WixToolset.Extensibility; 8 using WixToolset.Extensibility;
8 using WixToolset.Extensibility.Services; 9 using WixToolset.Extensibility.Services;
@@ -10,6 +11,7 @@ namespace WixToolset.Core
10 public class WixToolsetServiceProvider : IServiceProvider 11 public class WixToolsetServiceProvider : IServiceProvider
11 { 12 {
12 private ExtensionManager extensionManager; 13 private ExtensionManager extensionManager;
14 private ParseHelper parseHelper;
13 private TupleDefinitionCreator tupleDefinitionCreator; 15 private TupleDefinitionCreator tupleDefinitionCreator;
14 16
15 public object GetService(Type serviceType) 17 public object GetService(Type serviceType)
@@ -58,6 +60,11 @@ namespace WixToolset.Core
58 return this.tupleDefinitionCreator = this.tupleDefinitionCreator ?? new TupleDefinitionCreator(this); 60 return this.tupleDefinitionCreator = this.tupleDefinitionCreator ?? new TupleDefinitionCreator(this);
59 } 61 }
60 62
63 if (serviceType == typeof(IParseHelper))
64 {
65 return this.parseHelper = this.parseHelper ?? new ParseHelper(this);
66 }
67
61 throw new ArgumentException($"Unknown service type: {serviceType.Name}", nameof(serviceType)); 68 throw new ArgumentException($"Unknown service type: {serviceType.Name}", nameof(serviceType));
62 } 69 }
63 } 70 }