@joebigelow / wix / commits / aa6f15ec

Refactor CompilerCore to ParserHelper and other clean up

Rob Mensching committed Nov 14, 2017 at 23:21 UTC aa6f15ec4998a77622fafe9c510b3b547c595679
19 files changed +946 -747
src/WixToolset.Core/AppCommon.cs
+1 -2
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections.Specialized;
7 - using System.Configuration;
7 using System.Globalization;
8 using System.IO;
9 using System.Reflection;
src/WixToolset.Core/BinderCore.cs deleted
-44
@@ -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 -
3 -namespace 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 -}
src/WixToolset.Core/Common.cs
+4 -3
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 using System.Diagnostics;
@@ -13,6 +13,7 @@ namespace WixToolset
13 using System.Xml;
14 using System.Xml.Linq;
15 using WixToolset.Data;
16 + using WixToolset.Extensibility;
17
18 /// <summary>
19 /// Common Wix utility methods and types.
@@ -766,8 +767,8 @@ namespace WixToolset
767 /// <returns>The attribute's YesNoType value.</returns>
768 internal static string GetInnerText(XElement node)
769 {
769 - XText text = node.Nodes().Where(n => XmlNodeType.Text == n.NodeType || XmlNodeType.CDATA == n.NodeType).Cast<XText>().FirstOrDefault();
770 - return (null == text) ? null : text.Value;
770 + var text = node.Nodes().Where(n => XmlNodeType.Text == n.NodeType || XmlNodeType.CDATA == n.NodeType).Cast<XText>().FirstOrDefault();
771 + return text?.Value;
772 }
773
774 /// <summary>
src/WixToolset.Core/Compiler.cs
+5 -8
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections;
@@ -11,7 +11,6 @@ namespace WixToolset
11 using System.IO;
12 using System.Text.RegularExpressions;
13 using System.Xml.Linq;
14 - using WixToolset.Core;
14 using WixToolset.Core.Native;
15 using WixToolset.Data;
16 using WixToolset.Data.Tuples;
@@ -22,7 +21,6 @@ namespace WixToolset
21 /// <summary>
22 /// Compiler of the WiX toolset.
23 /// </summary>
25 - [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
24 public sealed class Compiler
25 {
26 public const string UpgradeDetectedProperty = "WIX_UPGRADE_DETECTED";
@@ -40,7 +38,7 @@ namespace WixToolset
38 private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder";
39 private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource";
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
42 // but if they are false you cannot not be sure they will not end
43 // up a product or module. Use these flags carefully.
44 private bool compilingModule;
@@ -123,16 +121,15 @@ namespace WixToolset
121 // Try to compile it.
122 try
123 {
126 - var creator = context.ServiceProvider.GetService<ITupleDefinitionCreator>();
124 + var parseHelper = context.ServiceProvider.GetService<IParseHelper>();
125
128 - this.Core = new CompilerCore(target, creator, extensionsByNamespace);
129 - this.Core.CurrentPlatform = this.Context.Platform;
126 + this.Core = new CompilerCore(target, parseHelper, extensionsByNamespace);
127 this.Core.ShowPedanticMessages = this.ShowPedanticMessages;
128 this.componentIdPlaceholdersResolver = new WixVariableResolver();
129
130 // parse the document
131 var source = context.Source;
135 - SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(source.Root);
132 + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(source.Root);
133 if ("Wix" == source.Root.Name.LocalName)
134 {
135 if (CompilerCore.WixNamespace == source.Root.Name.Namespace)
src/WixToolset.Core/CompilerCore.cs
+92 -668
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections;
@@ -17,6 +17,7 @@ namespace WixToolset
17 using WixToolset.Data;
18 using WixToolset.Data.Tuples;
19 using WixToolset.Extensibility;
20 + using WixToolset.Extensibility.Services;
21 using Wix = WixToolset.Data.Serialize;
22
23 internal enum ValueListKind
@@ -40,7 +41,7 @@ namespace WixToolset
41 /// <summary>
42 /// Core class for the compiler.
43 /// </summary>
43 - internal sealed class CompilerCore //: ICompilerCore
44 + internal sealed class CompilerCore
45 {
46 internal static readonly XNamespace W3SchemaPrefix = "http://www.w3.org/";
47 internal static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs";
@@ -50,19 +51,6 @@ namespace WixToolset
51 private const string IllegalLongFilenameCharacters = @"[\\\?|><:/\*""]"; // illegal: \ ? | > < : / * "
52 private static readonly Regex IllegalLongFilename = new Regex(IllegalLongFilenameCharacters, RegexOptions.Compiled);
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 -
54 public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB
55 public const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB
56 public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB)
@@ -141,7 +129,7 @@ namespace WixToolset
129 });
130
131 private Dictionary<XNamespace, ICompilerExtension> extensions;
144 - private ITupleDefinitionCreator creator;
132 + private IParseHelper parseHelper;
133 private Intermediate intermediate;
134
135 private HashSet<string> activeSectionInlinedDirectoryIds;
@@ -152,10 +140,10 @@ namespace WixToolset
140 /// </summary>
141 /// <param name="intermediate">The Intermediate object representing compiled source document.</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)
144 {
145 this.extensions = extensions;
158 - this.creator = creator;
146 + this.parseHelper = parseHelper;
147 this.intermediate = intermediate;
148 }
149
@@ -165,12 +153,6 @@ namespace WixToolset
153 /// <value>The section the compiler is currently emitting symbols into.</value>
154 public IntermediateSection ActiveSection { get; private set; }
155
168 - /// <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 -
156 /// <summary>
157 /// Gets whether the compiler core encountered an error while processing.
158 /// </summary>
@@ -231,12 +213,7 @@ namespace WixToolset
213 /// <returns>true if the filename is ambiguous; false otherwise.</returns>
214 public static bool IsAmbiguousFilename(string filename)
215 {
234 - if (null == filename || 0 == filename.Length)
235 - {
236 - return false;
237 - }
238 -
239 - return CompilerCore.AmbiguousFilename.IsMatch(filename);
216 + return String.IsNullOrEmpty(filename) ? false : CompilerCore.AmbiguousFilename.IsMatch(filename);
217 }
218
219 /// <summary>
@@ -246,7 +223,7 @@ namespace WixToolset
223 /// <returns>true if the value is an identifier; false otherwise.</returns>
224 public bool IsValidIdentifier(string value)
225 {
249 - return Common.IsIdentifier(value);
226 + return this.parseHelper.IsValidIdentifier(value);
227 }
228
229 /// <summary>
@@ -256,14 +233,7 @@ namespace WixToolset
233 /// <returns>True if the identifier is a valid loc identifier.</returns>
234 public bool IsValidLocIdentifier(string identifier)
235 {
259 - if (String.IsNullOrEmpty(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);
236 + return this.parseHelper.IsValidIdentifier(identifier);
237 }
238
239 /// <summary>
@@ -275,34 +245,7 @@ namespace WixToolset
245 /// <returns>True if the filename is a valid long filename</returns>
246 public bool IsValidLongFilename(string filename, bool allowWildcards = false, bool allowRelative = false)
247 {
278 - if (String.IsNullOrEmpty(filename))
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 - }
248 + return this.parseHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
249 }
250
251 /// <summary>
@@ -313,7 +256,7 @@ namespace WixToolset
256 /// <returns>True if the filename is a valid short filename</returns>
257 public bool IsValidShortFilename(string filename, bool allowWildcards)
258 {
316 - return Common.IsValidShortFilename(filename, allowWildcards);
259 + return this.parseHelper.IsValidShortFilename(filename, allowWildcards);
260 }
261
262 /// <summary>
@@ -337,52 +280,7 @@ namespace WixToolset
280 /// <returns>The generated 8.3-compliant short file/directory name.</returns>
281 public string CreateShortName(string longName, bool keepExtension, bool allowWildcards, params string[] args)
282 {
340 - // canonicalize the long name if its not a localization identifier (they are case-sensitive)
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();
283 + return this.parseHelper.CreateShortName(longName, keepExtension, allowWildcards, args);
284 }
285
286 /// <summary>
@@ -422,8 +320,7 @@ namespace WixToolset
320 /// <returns>The node's inner text trimmed.</returns>
321 public string GetTrimmedInnerText(XElement element)
322 {
425 - string value = Common.GetInnerText(element);
426 - return (null == value) ? null : value.Trim();
323 + return this.parseHelper.GetTrimmedInnerText(element);
324 }
325
326 /// <summary>
@@ -431,23 +328,9 @@ namespace WixToolset
328 /// </summary>
329 /// <param name="element">The element to ensure inner text is a condition.</param>
330 /// <returns>The value converted into a safe condition.</returns>
434 - [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
331 public string GetConditionInnerText(XElement element)
332 {
437 - string value = element.Value;
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;
333 + return this.parseHelper.GetConditionInnerText(element);
334 }
335
336 /// <summary>
@@ -458,7 +341,7 @@ namespace WixToolset
341 /// <returns>The generated GUID for the given namespace and value.</returns>
342 public string CreateGuid(Guid namespaceGuid, string value)
343 {
461 - return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant();
344 + return this.parseHelper.CreateGuid(namespaceGuid, value);
345 }
346
347 /// <summary>
@@ -510,46 +393,7 @@ namespace WixToolset
393 /// <returns>Identifier of the leaf directory created.</returns>
394 public string CreateDirectoryReferenceFromInlineSyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, string parentId)
395 {
513 - string id = null;
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;
396 + return this.parseHelper.CreateDirectoryReferenceFromInlineSyntax(this.ActiveSection, sourceLineNumbers, attribute, parentId);
397 }
398
399 /// <summary>
@@ -566,56 +410,6 @@ namespace WixToolset
410 patchReferenceRow.Set(1, String.Join("/", primaryKeys));
411 }
412
569 - /// <summary>
570 - /// Creates a Registry row in the active section.
571 - /// </summary>
572 - /// <param name="sourceLineNumbers">Source and line number of the current row.</param>
573 - /// <param name="root">The registry entry root.</param>
574 - /// <param name="key">The registry entry key.</param>
575 - /// <param name="name">The registry entry name.</param>
576 - /// <param name="value">The registry entry value.</param>
577 - /// <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 -
413 /// <summary>
414 /// Creates a Registry row in the active section.
415 /// </summary>
@@ -627,7 +421,7 @@ namespace WixToolset
421 /// <param name="componentId">The component which will control installation/uninstallation of the registry entry.</param>
422 public Identifier CreateRegistryRow(SourceLineNumber sourceLineNumbers, int root, string key, string name, string value, string componentId)
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);
425 }
426
427 /// <summary>
@@ -646,9 +440,7 @@ namespace WixToolset
440 // If this simple reference hasn't been added to the active section already, add it.
441 if (this.activeSectionSimpleReferences.Add(id))
442 {
649 - var wixSimpleReferenceRow = (WixSimpleReferenceTuple)this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixSimpleReference);
650 - wixSimpleReferenceRow.Table = tableName;
651 - wixSimpleReferenceRow.PrimaryKeys = joinedKeys;
443 + this.parseHelper.CreateSimpleReference(this.ActiveSection, sourceLineNumbers, tableName, primaryKeys);
444 }
445 }
446 }
@@ -665,21 +457,7 @@ namespace WixToolset
457 {
458 if (!this.EncounteredError)
459 {
668 - if (null == parentId || ComplexReferenceParentType.Unknown == parentType)
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;
460 + this.parseHelper.CreateWixGroupRow(this.ActiveSection, sourceLineNumbers, parentType, parentId, childType, childId);
461 }
462 }
463
@@ -693,16 +471,7 @@ namespace WixToolset
471 {
472 if (!this.EncounteredError)
473 {
696 - var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.WixEnsureTable);
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 - }
474 + this.parseHelper.EnsureTable(this.ActiveSection, sourceLineNumbers, tableName);
475 }
476 }
477
@@ -716,7 +485,7 @@ namespace WixToolset
485 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
486 public string GetAttributeValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, EmptyRule emptyRule = EmptyRule.CanBeWhitespaceOnly)
487 {
719 - return Common.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
488 + return this.parseHelper.GetAttributeValue(sourceLineNumbers, attribute, emptyRule);
489 }
490
491 /// <summary>
@@ -800,7 +569,7 @@ namespace WixToolset
569 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
570 public int GetAttributeIntegerValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, int minimum, int maximum)
571 {
803 - return Common.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum);
572 + return this.parseHelper.GetAttributeIntegerValue(sourceLineNumbers, attribute, minimum, maximum);
573 }
574
575 /// <summary>
@@ -813,39 +582,7 @@ namespace WixToolset
582 /// <returns>The attribute's long value or a special value if an error occurred during conversion.</returns>
583 public long GetAttributeLongValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, long minimum, long maximum)
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.");
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;
585 + return this.parseHelper.GetAttributeLongValue(sourceLineNumbers, attribute, minimum, maximum);
586 }
587
588 /// <summary>
@@ -956,73 +693,9 @@ namespace WixToolset
693 /// <param name="generatable">Determines whether the guid can be automatically generated.</param>
694 /// <param name="canBeEmpty">If true, no error is raised on empty value. If false, an error is raised.</param>
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")]
696 public string GetAttributeGuidValue(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool generatable = false, bool canBeEmpty = false)
697 {
963 - if (null == attribute)
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;
698 + return this.parseHelper.GetAttributeGuidValue(sourceLineNumbers, attribute, generatable, canBeEmpty);
699 }
700
701 /// <summary>
@@ -1033,27 +706,7 @@ namespace WixToolset
706 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns>
707 public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAttribute attribute)
708 {
1036 - string value = Common.GetAttributeValue(sourceLineNumbers, attribute, EmptyRule.CanBeEmpty);
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);
709 + return this.parseHelper.GetAttributeIdentifier(sourceLineNumbers, attribute);
710 }
711
712 /// <summary>
@@ -1064,7 +717,7 @@ namespace WixToolset
717 /// <returns>The attribute's identifier value or a special value if an error occurred.</returns>
718 public string GetAttributeIdentifierValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
719 {
1067 - return Common.GetAttributeIdentifierValue(sourceLineNumbers, attribute);
720 + return this.parseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attribute);
721 }
722
723 /// <summary>
@@ -1075,23 +728,7 @@ namespace WixToolset
728 /// <returns>The attribute's YesNoType value.</returns>
729 public YesNoType GetAttributeYesNoValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
730 {
1078 - string value = this.GetAttributeValue(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;
731 + return this.parseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute);
732 }
733
734 /// <summary>
@@ -1103,28 +740,7 @@ namespace WixToolset
740 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
741 public YesNoDefaultType GetAttributeYesNoDefaultValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
742 {
1106 - string value = this.GetAttributeValue(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;
743 + return this.parseHelper.GetAttributeYesNoDefaultValue(sourceLineNumbers, attribute);
744 }
745
746 /// <summary>
@@ -1202,41 +818,7 @@ namespace WixToolset
818 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
819 public string GetAttributeLongFilename(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool allowWildcards = false, bool allowRelative = false)
820 {
1205 - if (null == attribute)
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;
821 + return this.parseHelper.GetAttributeLongFilename(sourceLineNumbers, attribute, allowWildcards, allowRelative);
822 }
823
824 /// <summary>
@@ -1247,31 +829,7 @@ namespace WixToolset
829 /// <returns>The attribute's version value.</returns>
830 public string GetAttributeVersionValue(SourceLineNumber sourceLineNumbers, XAttribute attribute)
831 {
1250 - string value = this.GetAttributeValue(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;
832 + return this.parseHelper.GetAttributeVersionValue(sourceLineNumbers, attribute);
833 }
834
835 /// <summary>
@@ -1453,7 +1011,7 @@ namespace WixToolset
1011 /// <returns>True if a property is found in the string.</returns>
1012 public bool ContainsProperty(string possibleProperty)
1013 {
1456 - return Common.ContainsProperty(possibleProperty);
1014 + return this.parseHelper.ContainsProperty(possibleProperty);
1015 }
1016
1017 /// <summary>
@@ -1465,8 +1023,7 @@ namespace WixToolset
1023 [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")]
1024 public Identifier CreateIdentifier(string prefix, params string[] args)
1025 {
1468 - string id = Common.GenerateIdentifier(prefix, args);
1469 - return new Identifier(id, AccessModifier.Private);
1026 + return this.parseHelper.CreateIdentifier(prefix, args);
1027 }
1028
1029 /// <summary>
@@ -1476,8 +1033,7 @@ namespace WixToolset
1033 /// <returns></returns>
1034 public Identifier CreateIdentifierFromFilename(string filename)
1035 {
1479 - string id = Common.GetIdentifierFromName(filename);
1480 - return new Identifier(id, AccessModifier.Private);
1036 + return this.parseHelper.CreateIdentifierFromFilename(filename);
1037 }
1038
1039 /// <summary>
@@ -1488,22 +1044,7 @@ namespace WixToolset
1044 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
1045 public void ParseExtensionAttribute(XElement element, XAttribute attribute, IDictionary<string, string> context = null)
1046 {
1491 - // Ignore attributes defined by the W3C because we'll assume they are always right.
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 - }
1047 + this.parseHelper.ParseExtensionAttribute(this.extensions.Values, this.intermediate, this.ActiveSection, element, attribute, context);
1048 }
1049
1050 /// <summary>
@@ -1514,16 +1055,7 @@ namespace WixToolset
1055 /// <param name="context">Extra information about the context in which this element is being parsed.</param>
1056 public void ParseExtensionElement(XElement parentElement, XElement element, IDictionary<string, string> context = null)
1057 {
1517 - if (this.TryFindExtension(element.Name.Namespace, out var extension))
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 - }
1058 + this.parseHelper.ParseExtensionElement(this.extensions.Values, this.intermediate, this.ActiveSection, parentElement, element, context);
1059 }
1060
1061 /// <summary>
@@ -1532,17 +1064,7 @@ namespace WixToolset
1064 /// <param name="element">Element to parse children.</param>
1065 public void ParseForExtensionElements(XElement element)
1066 {
1535 - foreach (XElement child in element.Elements())
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 - }
1067 + this.parseHelper.ParseForExtensionElements(this.extensions.Values, this.intermediate, this.ActiveSection, element);
1068 }
1069
1070 /// <summary>
@@ -1553,20 +1075,7 @@ namespace WixToolset
1075 /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param>
1076 public ComponentKeyPath ParsePossibleKeyPathExtensionElement(XElement parentElement, XElement element, IDictionary<string, string> context)
1077 {
1556 - ComponentKeyPath keyPath = null;
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;
1078 + return this.parseHelper.ParsePossibleKeyPathExtensionElement(this.extensions.Values, this.intermediate, this.ActiveSection, parentElement, element, context);
1079 }
1080
1081 /// <summary>
@@ -1576,8 +1085,7 @@ namespace WixToolset
1085 /// <param name="attribute">The unexpected attribute.</param>
1086 public void UnexpectedAttribute(XElement element, XAttribute attribute)
1087 {
1579 - SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(element);
1580 - Common.UnexpectedAttribute(sourceLineNumbers, attribute);
1088 + this.parseHelper.UnexpectedAttribute(element, attribute);
1089 }
1090
1091 /// <summary>
@@ -1587,9 +1095,7 @@ namespace WixToolset
1095 /// <param name="childElement">The unexpected child element.</param>
1096 public void UnexpectedElement(XElement parentElement, XElement childElement)
1097 {
1590 - SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(childElement);
1591 -
1592 - this.OnMessage(WixErrors.UnexpectedElement(sourceLineNumbers, parentElement.Name.LocalName, childElement.Name.LocalName));
1098 + this.parseHelper.UnexpectedElement(parentElement, childElement);
1099 }
1100
1101 /// <summary>
@@ -1666,30 +1172,6 @@ namespace WixToolset
1172 return section;
1173 }
1174
1669 - /// <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 -
1175 /// <summary>
1176 /// Creates WixComplexReference and WixGroup rows in the active section.
1177 /// </summary>
@@ -1702,8 +1184,7 @@ namespace WixToolset
1184 /// <param name="isPrimary">Whether the child is primary.</param>
1185 public void CreateComplexReference(SourceLineNumber sourceLineNumbers, ComplexReferenceParentType parentType, string parentId, string parentLanguage, ComplexReferenceChildType childType, string childId, bool isPrimary)
1186 {
1705 - this.CreateWixComplexReferenceRow(sourceLineNumbers, parentType, parentId, parentLanguage, childType, childId, isPrimary);
1706 - this.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, childType, childId);
1187 + this.parseHelper.CreateComplexReference(this.ActiveSection, sourceLineNumbers, parentType, parentId, parentLanguage, childType, childId, isPrimary);
1188 }
1189
1190 /// <summary>
@@ -1719,56 +1200,57 @@ namespace WixToolset
1200 /// <returns>Identifier for the newly created row.</returns>
1201 internal Identifier CreateDirectoryRow(SourceLineNumber sourceLineNumbers, Identifier id, string parentId, string name, string shortName = null, string sourceName = null, string shortSourceName = null)
1202 {
1722 - string defaultDir = null;
1723 -
1724 - if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false))
1725 - {
1726 - defaultDir = name;
1727 - }
1728 - else
1729 - {
1730 - if (String.IsNullOrEmpty(shortName))
1731 - {
1732 - shortName = this.CreateShortName(name, false, false, "Directory", parentId);
1733 - }
1734 -
1735 - defaultDir = String.Concat(shortName, "|", name);
1736 - }
1737 -
1738 - if (!String.IsNullOrEmpty(sourceName))
1739 - {
1740 - if (this.IsValidShortFilename(sourceName, false))
1741 - {
1742 - defaultDir = String.Concat(defaultDir, ":", sourceName);
1743 - }
1744 - else
1745 - {
1746 - if (String.IsNullOrEmpty(shortSourceName))
1747 - {
1748 - shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId);
1749 - }
1750 -
1751 - defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName);
1752 - }
1753 - }
1754 -
1755 - // 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
1757 - // but bloat the intermediate and ultimately make the linker do "busy work").
1758 - if (null == id)
1759 - {
1760 - id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName);
1761 -
1762 - if (!this.activeSectionInlinedDirectoryIds.Add(id.Id))
1763 - {
1764 - return id;
1765 - }
1766 - }
1767 -
1768 - var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.Directory, id);
1769 - row.Set(1, parentId);
1770 - row.Set(2, defaultDir);
1771 - return id;
1203 + //string defaultDir = null;
1204 +
1205 + //if (name.Equals("SourceDir") || this.IsValidShortFilename(name, false))
1206 + //{
1207 + // defaultDir = name;
1208 + //}
1209 + //else
1210 + //{
1211 + // if (String.IsNullOrEmpty(shortName))
1212 + // {
1213 + // shortName = this.CreateShortName(name, false, false, "Directory", parentId);
1214 + // }
1215 +
1216 + // defaultDir = String.Concat(shortName, "|", name);
1217 + //}
1218 +
1219 + //if (!String.IsNullOrEmpty(sourceName))
1220 + //{
1221 + // if (this.IsValidShortFilename(sourceName, false))
1222 + // {
1223 + // defaultDir = String.Concat(defaultDir, ":", sourceName);
1224 + // }
1225 + // else
1226 + // {
1227 + // if (String.IsNullOrEmpty(shortSourceName))
1228 + // {
1229 + // shortSourceName = this.CreateShortName(sourceName, false, false, "Directory", parentId);
1230 + // }
1231 +
1232 + // defaultDir = String.Concat(defaultDir, ":", shortSourceName, "|", sourceName);
1233 + // }
1234 + //}
1235 +
1236 + //// For anonymous directories, create the identifier. If this identifier already exists in the
1237 + //// active section, bail so we don't add duplicate anonymous directory rows (which are legal
1238 + //// but bloat the intermediate and ultimately make the linker do "busy work").
1239 + //if (null == id)
1240 + //{
1241 + // id = this.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName);
1242 +
1243 + // if (!this.activeSectionInlinedDirectoryIds.Add(id.Id))
1244 + // {
1245 + // return id;
1246 + // }
1247 + //}
1248 +
1249 + //var row = this.CreateRow(sourceLineNumbers, TupleDefinitionType.Directory, id);
1250 + //row.Set(1, parentId);
1251 + //row.Set(2, defaultDir);
1252 + //return id;
1253 + return this.parseHelper.CreateDirectoryRow(this.ActiveSection, sourceLineNumbers, id, parentId, name, shortName, sourceName, shortSourceName, this.activeSectionInlinedDirectoryIds);
1254 }
1255
1256 /// <summary>
@@ -1780,65 +1262,7 @@ namespace WixToolset
1262 /// <returns>Inline directory syntax split into array of strings or null if the syntax did not parse.</returns>
1263 internal string[] GetAttributeInlineDirectorySyntax(SourceLineNumber sourceLineNumbers, XAttribute attribute, bool resultUsedToCreateReference = false)
1264 {
1783 - string[] result = null;
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;
1265 + return this.parseHelper.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attribute, resultUsedToCreateReference);
1266 }
1267
1268 /// <summary>
src/WixToolset.Core/Converter.cs
+1 -1
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections.Generic;
src/WixToolset.Core/Extensibility/HeatExtension.cs
+1 -1
@@ -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.
2
3 -namespace WixToolset.Extensibility
3 +namespace WixToolset.Core.Extensibility
4 {
5 using System;
6 using System.IO;
src/WixToolset.Core/Extensibility/IHeatCore.cs
+1 -1
@@ -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.
2
3 -namespace WixToolset.Extensibility
3 +namespace WixToolset.Core.Extensibility
4 {
5 using WixToolset.Data;
6
src/WixToolset.Core/Extensibility/MutatorExtension.cs
+1 -1
@@ -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.
2
3 -namespace WixToolset.Extensibility
3 +namespace WixToolset.Core.Extensibility
4 {
5 using System;
6 using System.Collections.Generic;
src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs new
+824
@@ -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 +
3 +namespace WixToolset.Core.ExtensibilityServices
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Diagnostics;
8 + using System.Globalization;
9 + using System.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 +}
src/WixToolset.Core/ExtensibilityServices/TupleDefinitionCreator.cs renamed
+1 -1
@@ -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.
2
3 -namespace WixToolset.Core
3 +namespace WixToolset.Core.ExtensibilityServices
4 {
5 using System;
6 using System.Collections.Generic;
src/WixToolset.Core/HarvesterCore.cs
+1 -2
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 using System.Diagnostics.CodeAnalysis;
7 using System.IO;
8 using WixToolset.Data;
9 - using Wix = WixToolset.Data.Serialize;
9
10 /// <summary>
11 /// The WiX Toolset harvester core.
src/WixToolset.Core/HeatCore.cs
+2 -2
@@ -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.
2
3 -namespace WixToolset.Tools
3 +namespace WixToolset.Core
4 {
5 + using WixToolset.Core.Extensibility;
6 using WixToolset.Data;
6 - using WixToolset.Extensibility;
7
8 /// <summary>
9 /// The WiX Toolset Harvester application core.
src/WixToolset.Core/Link/WixGroupingOrdering.cs
+1 -3
@@ -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.
2
3 -namespace WixToolset.Link
3 +namespace WixToolset.Core.Link
4 {
5 using System;
6 - using System.Collections;
6 using System.Collections.ObjectModel;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.Linq;
11 using System.Text;
13 - using WixToolset.Extensibility;
12 using WixToolset.Data;
13 using WixToolset.Data.Tuples;
14
src/WixToolset.Core/Localizer.cs
+1 -1
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections.Generic;
src/WixToolset.Core/Mutator.cs
+2 -2
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections;
7 - using WixToolset.Extensibility;
7 + using WixToolset.Core.Extensibility;
8 using Wix = WixToolset.Data.Serialize;
9
10 /// <summary>
src/WixToolset.Core/Preprocessor.cs
+1 -2
@@ -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.
2
3 -namespace WixToolset
3 +namespace WixToolset.Core
4 {
5 using System;
6 - using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization;
src/WixToolset.Core/WixToolsetServiceProvider.cs
+7
@@ -3,6 +3,7 @@
3 namespace WixToolset.Core
4 {
5 using System;
6 + using WixToolset.Core.ExtensibilityServices;
7 using WixToolset.Data;
8 using WixToolset.Extensibility;
9 using WixToolset.Extensibility.Services;
@@ -10,6 +11,7 @@ namespace WixToolset.Core
11 public class WixToolsetServiceProvider : IServiceProvider
12 {
13 private ExtensionManager extensionManager;
14 + private ParseHelper parseHelper;
15 private TupleDefinitionCreator tupleDefinitionCreator;
16
17 public object GetService(Type serviceType)
@@ -58,6 +60,11 @@ namespace WixToolset.Core
60 return this.tupleDefinitionCreator = this.tupleDefinitionCreator ?? new TupleDefinitionCreator(this);
61 }
62
63 + if (serviceType == typeof(IParseHelper))
64 + {
65 + return this.parseHelper = this.parseHelper ?? new ParseHelper(this);
66 + }
67 +
68 throw new ArgumentException($"Unknown service type: {serviceType.Name}", nameof(serviceType));
69 }
70 }
src/WixToolset.Data.WindowsInstaller/Output.cs
-5
@@ -186,7 +186,6 @@ namespace WixToolset.Data
186
187 bool empty = reader.IsEmptyElement;
188 Output output = new Output(SourceLineNumber.CreateFromUri(reader.BaseURI));
189 - SectionType sectionType = SectionType.Unknown;
189 Version version = null;
190
191 while (reader.MoveToNextAttribute())
@@ -201,22 +200,18 @@ namespace WixToolset.Data
200 {
201 case "Bundle":
202 output.Type = OutputType.Bundle;
204 - sectionType = SectionType.Bundle;
203 break;
204 case "Module":
205 output.Type = OutputType.Module;
208 - sectionType = SectionType.Module;
206 break;
207 case "Patch":
208 output.Type = OutputType.Patch;
209 break;
210 case "PatchCreation":
211 output.Type = OutputType.PatchCreation;
215 - sectionType = SectionType.PatchCreation;
212 break;
213 case "Product":
214 output.Type = OutputType.Product;
219 - sectionType = SectionType.Product;
215 break;
216 case "Transform":
217 output.Type = OutputType.Transform;