@joebigelow / wix / commits / 0d29fa5d

Fix deprecated $(loc.xxx) localization variable references.

Fixes wixtoolset/issues#6262

Ron Martin committed Mar 28, 2021 at 23:29 UTC 0d29fa5d042d56517aaefb21f4fc512516039377
2 files changed +102 -12
src/WixToolset.Converters/WixConverter.cs
+64 -12
@@ -179,6 +179,8 @@ namespace WixToolset.Converters
179 };
180
181 private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping;
182 + private readonly Regex DeprecatedPrefixRegex = new Regex(@"(?<=(^|[^\$])(\$\$)*)\$(?=\(loc\.[^.].*\))",
183 + RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture);
184
185 /// <summary>
186 /// Instantiate a new Converter class.
@@ -427,6 +429,13 @@ namespace WixToolset.Converters
429 {
430 if (node is XText text)
431 {
432 + if (null != text.Value)
433 + {
434 + if (this.TryFixDeprecatedLocalizationPrefixes(node, text.Value, out var newValue, ConverterTestType.DeprecatedLocalizationVariablePrefixInTextValue))
435 + {
436 + text.Value = newValue;
437 + }
438 + }
439 if (!String.IsNullOrWhiteSpace(text.Value))
440 {
441 text.Value = text.Value.Trim();
@@ -464,6 +473,20 @@ namespace WixToolset.Converters
473 }
474 }
475
476 + private bool TryFixDeprecatedLocalizationPrefixes(XNode node, string value, out string newValue, ConverterTestType testType)
477 + {
478 + newValue = this.DeprecatedPrefixRegex.Replace(value, "!");
479 +
480 + if (object.ReferenceEquals(newValue, value))
481 + {
482 + return false;
483 + }
484 +
485 + var message = testType == ConverterTestType.DeprecatedLocalizationVariablePrefixInTextValue ? "The prefix on the localization variable in the inner text is incorrect." : "The prefix on the localization variable in the attribute value is incorrect.";
486 +
487 + return this.OnError(testType, node, message);
488 + }
489 +
490 private void EnsurePrecedingWhitespaceCorrect(XText whitespace, XNode node, int level, ConverterTestType testType)
491 {
492 if (!WixConverter.LeadingWhitespaceValid(this.IndentationAmount, level, whitespace.Value))
@@ -492,25 +515,40 @@ namespace WixToolset.Converters
515
516 private void ConvertElement(XElement element)
517 {
495 - // Gather any deprecated namespaces, then update this element tree based on those deprecations.
518 var deprecatedToUpdatedNamespaces = new Dictionary<XNamespace, XNamespace>();
519
498 - foreach (var declaration in element.Attributes().Where(a => a.IsNamespaceDeclaration))
520 + foreach (var attribute in element.Attributes())
521 {
500 - if (element.Name == Wix3ElementName || element.Name == Include3ElementName)
501 - {
502 - this.SourceVersion = 3;
503 - }
504 - else if (element.Name == Wix4ElementName || element.Name == Include4ElementName)
522 + if (attribute.IsNamespaceDeclaration)
523 {
506 - this.SourceVersion = 4;
507 - }
524 + // Gather any deprecated namespaces, then update this element tree based on those deprecations.
525 + var declaration = attribute;
526
509 - if (WixConverter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out var ns))
527 + if (element.Name == Wix3ElementName || element.Name == Include3ElementName)
528 + {
529 + this.SourceVersion = 3;
530 + }
531 + else if (element.Name == Wix4ElementName || element.Name == Include4ElementName)
532 + {
533 + this.SourceVersion = 4;
534 + }
535 +
536 + if (WixConverter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out var ns))
537 + {
538 + if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName))
539 + {
540 + deprecatedToUpdatedNamespaces.Add(declaration.Value, ns);
541 + }
542 + }
543 + }
544 + else
545 {
511 - if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName))
546 + if (null != attribute.Value)
547 {
513 - deprecatedToUpdatedNamespaces.Add(declaration.Value, ns);
548 + if (this.TryFixDeprecatedLocalizationPrefixes(element, attribute.Value, out var newValue, ConverterTestType.DeprecatedLocalizationVariablePrefixInAttributeValue))
549 + {
550 + attribute.Value = newValue;
551 + }
552 }
553 }
554 }
@@ -1958,11 +1996,15 @@ namespace WixToolset.Converters
1996 /// </summary>
1997 WhitespacePrecedingEndElementWrong,
1998
1999 + // Before this point, ignore errors on convert operation
2000 +
2001 /// <summary>
2002 /// Displayed when the XML declaration is present in the source file.
2003 /// </summary>
2004 DeclarationPresent,
2005
2006 + // After this point, ignore errors on format operation
2007 +
2008 /// <summary>
2009 /// Displayed when the xmlns attribute is missing from the document element.
2010 /// </summary>
@@ -1973,6 +2015,16 @@ namespace WixToolset.Converters
2015 /// </summary>
2016 XmlnsValueWrong,
2017
2018 + /// <summary>
2019 + /// Displayed when inner text contains a deprecated $(loc.xxx) reference.
2020 + /// </summary>
2021 + DeprecatedLocalizationVariablePrefixInTextValue,
2022 +
2023 + /// <summary>
2024 + /// Displayed when an attribute value contains a deprecated $(loc.xxx) reference.
2025 + /// </summary>
2026 + DeprecatedLocalizationVariablePrefixInAttributeValue,
2027 +
2028 /// <summary>
2029 /// Assign an identifier to a File element when on Id attribute is specified.
2030 /// </summary>
src/test/WixToolsetTest.Converters/ConverterFixture.cs
+38
@@ -407,5 +407,43 @@ namespace WixToolsetTest.Converters
407 Assert.Equal(2, errors);
408 Assert.Equal(expected, actual);
409 }
410 +
411 + [Fact]
412 + public void CanConvertDeprecatedPrefix()
413 + {
414 + var parse = String.Join(Environment.NewLine,
415 + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
416 + "<Fragment>",
417 + "<ComponentGroup Id=\"$(loc.Variable)\" />",
418 + "<ComponentGroup Id=\"$$(loc.Variable)\" />",
419 + "<ComponentGroup Id=\"$$$(loc.Variable)\" />",
420 + "<ComponentGroup Id=\"$$$$(loc.Variable)\" />",
421 + "<ComponentGroup Id=\"$$$$$(loc.Variable)\" />",
422 + "</Fragment>",
423 + "</Wix>");
424 +
425 + var expected = String.Join(Environment.NewLine,
426 + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
427 + "<Fragment>",
428 + "<ComponentGroup Id=\"!(loc.Variable)\" />",
429 + "<ComponentGroup Id=\"$$(loc.Variable)\" />",
430 + "<ComponentGroup Id=\"$$!(loc.Variable)\" />",
431 + "<ComponentGroup Id=\"$$$$(loc.Variable)\" />",
432 + "<ComponentGroup Id=\"$$$$!(loc.Variable)\" />",
433 + "</Fragment>",
434 + "</Wix>");
435 +
436 + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
437 +
438 + var messaging = new MockMessaging();
439 + var converter = new WixConverter(messaging, 2, null, null);
440 +
441 + var errors = converter.ConvertDocument(document);
442 +
443 + var actual = UnformattedDocumentString(document);
444 +
445 + Assert.Equal(3, errors);
446 + Assert.Equal(expected, actual);
447 + }
448 }
449 }