Resolve issues with inner text
Fixes wixtoolset/issues#7739
cpuwzd committed
Dec 12, 2023 at 09:49 UTC
34132d843795595ccda8cbd5329e290c23b5dbce
2 files changed
+625
-98
src/wix/WixToolset.Converters/WixConverter.cs
+294
-88
@@ -1033,17 +1033,18 @@ namespace WixToolset.Converters
1033
using (var lab = new ConversionLab(element))
1034
{
1035
var xConditions = element.Elements(ConditionElementName).ToList();
1036
- var comments = new List<XNode>();
1036
+ var collector = new InnerContentCollector();
1037
var conditions = new List<KeyValuePair<string, string>>();
1038
1039
foreach (var xCondition in xConditions)
1040
{
1041
var action = UppercaseFirstChar(xCondition.Attribute("Action")?.Value);
1042
+
1043
if (!String.IsNullOrEmpty(action) &&
1043
- TryGetInnerText(xCondition, out var text, out comments, comments) &&
1044
+ collector.CollectInnerTextAndCommentsForAttributeValue(xCondition, out string value) &&
1045
this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the '{1}Condition' attribute instead.", xCondition.Name.LocalName, action))
1046
{
1046
- conditions.Add(new KeyValuePair<string, string>(action, text));
1047
+ conditions.Add(new KeyValuePair<string, string>(action, value));
1048
}
1049
}
1050
@@ -1062,7 +1063,7 @@ namespace WixToolset.Converters
1063
}
1064
1065
lab.RemoveOrphanTextNodes();
1065
- lab.AddCommentsAsSiblings(comments);
1066
+ lab.AddCommentsAsSiblings(collector.Comments);
1067
}
1068
}
1069
@@ -1080,15 +1081,17 @@ namespace WixToolset.Converters
1081
var xCondition = element.Element(ConditionElementName);
1082
if (xCondition != null)
1083
{
1083
- if (TryGetInnerText(xCondition, out var text, out var comments) &&
1084
+ var collector = new InnerContentCollector();
1085
+
1086
+ if (collector.CollectInnerTextAndCommentsForAttributeValue(xCondition, out string value) &&
1087
this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Condition' attribute instead.", xCondition.Name.LocalName))
1088
{
1089
using (var lab = new ConversionLab(element))
1090
{
1091
xCondition.Remove();
1089
- element.Add(new XAttribute("Condition", text));
1092
+ element.Add(new XAttribute("Condition", value));
1093
lab.RemoveOrphanTextNodes();
1091
- lab.AddCommentsAsSiblings(comments);
1094
+ lab.AddCommentsAsSiblings(collector.Comments);
1095
}
1096
}
1097
}
@@ -1191,16 +1194,18 @@ namespace WixToolset.Converters
1194
if (xCondition != null)
1195
{
1196
var level = xCondition.Attribute("Level")?.Value;
1197
+ var collector = new InnerContentCollector();
1198
+
1199
if (!String.IsNullOrEmpty(level) &&
1195
- TryGetInnerText(xCondition, out var text, out var comments) &&
1200
+ collector.CollectInnerTextAndCommentsForAttributeValue(xCondition, out string value) &&
1201
this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Level' element instead.", xCondition.Name.LocalName))
1202
{
1203
using (var lab = new ConversionLab(xCondition))
1204
{
1205
lab.ReplaceTargetElement(new XElement(LevelElementName,
1206
new XAttribute("Value", level),
1202
- new XAttribute("Condition", text)));
1203
- lab.AddCommentsAsSiblings(comments);
1207
+ new XAttribute("Condition", value)));
1208
+ lab.AddCommentsAsSiblings(collector.Comments);
1209
}
1210
}
1211
}
@@ -1235,17 +1240,23 @@ namespace WixToolset.Converters
1240
private void ConvertLaunchConditionElement(XElement element)
1241
{
1242
var message = element.Attribute("Message")?.Value;
1243
+ var collector = new InnerContentCollector();
1244
1245
if (!String.IsNullOrEmpty(message) &&
1240
- TryGetInnerText(element, out var text, out var comments) &&
1246
+ collector.CollectInnerTextWithTrailingWhitespaceAndCommentsForAttributeValue(element, out string value) &&
1247
this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Launch' element instead.", element.Name.LocalName))
1248
{
1249
+ if (String.IsNullOrWhiteSpace(value))
1250
+ {
1251
+ value = String.Empty;
1252
+ }
1253
+
1254
using (var lab = new ConversionLab(element))
1255
{
1256
lab.ReplaceTargetElement(new XElement(LaunchElementName,
1246
- new XAttribute("Condition", text),
1257
+ new XAttribute("Condition", value),
1258
new XAttribute("Message", message)));
1248
- lab.AddCommentsAsSiblings(comments);
1259
+ lab.AddCommentsAsSiblings(collector.Comments);
1260
}
1261
}
1262
}
@@ -1290,7 +1301,8 @@ namespace WixToolset.Converters
1301
var xCondition = element.Element(ConditionElementName);
1302
if (xCondition != null)
1303
{
1293
- if (TryGetInnerText(xCondition, out var text, out var comments) &&
1304
+ var collector = new InnerContentCollector();
1305
+ if (collector.CollectInnerTextAndCommentsForAttributeValue(xCondition, out string value) &&
1306
this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Condition' attribute instead.", xCondition.Name.LocalName))
1307
{
1308
using (var lab = new ConversionLab(xCondition))
@@ -1299,9 +1311,9 @@ namespace WixToolset.Converters
1311
}
1312
using (var lab = new ConversionLab(element))
1313
{
1302
- element.Add(new XAttribute("Condition", text));
1314
+ element.Add(new XAttribute("Condition", value));
1315
lab.RemoveOrphanTextNodes();
1304
- lab.AddCommentsAsSiblings(comments);
1316
+ lab.AddCommentsAsSiblings(collector.Comments);
1317
}
1318
}
1319
}
@@ -1669,33 +1681,35 @@ namespace WixToolset.Converters
1681
1682
private void ConvertPublishElement(XElement element)
1683
{
1672
- if (TryGetInnerText(element, out var text, out var comments) &&
1684
+ var collector = new InnerContentCollector();
1685
+
1686
+ if (collector.CollectInnerTextAndCommentsForAttributeValue(element, out string value) &&
1687
this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Condition' attribute instead.", element.Name.LocalName))
1688
{
1689
using (var lab = new ConversionLab(element))
1690
{
1677
- if ("1" == text)
1691
+ if ("1" == value)
1692
{
1693
this.OnInformation(ConverterTestType.PublishConditionOneUnnecessary, element, "Adding Condition='1' on {0} elements is no longer necessary. Remove the Condition attribute.", element.Name.LocalName);
1694
}
1695
else
1696
{
1683
- element.Add(new XAttribute("Condition", text));
1697
+ element.Add(new XAttribute("Condition", value));
1698
}
1699
1700
lab.RemoveOrphanTextNodes();
1687
- lab.AddCommentsAsSiblings(comments);
1701
+ lab.AddCommentsAsSiblings(collector.Comments);
1702
}
1703
}
1704
1691
- var evnt = element.Attribute("Event")?.Value;
1692
- var value = element.Attribute("Value")?.Value;
1705
+ var eventName = element.Attribute("Event")?.Value;
1706
+ var eventValue = element.Attribute("Value")?.Value;
1707
1694
- if (evnt?.Equals("DoAction", StringComparison.OrdinalIgnoreCase) == true
1695
- && value?.StartsWith("WixUI", StringComparison.OrdinalIgnoreCase) == true
1708
+ if (eventName?.Equals("DoAction", StringComparison.OrdinalIgnoreCase) == true
1709
+ && eventValue?.StartsWith("WixUI", StringComparison.OrdinalIgnoreCase) == true
1710
&& this.OnInformation(ConverterTestType.CustomActionIdsIncludePlatformSuffix, element, "Custom action ids have changed in WiX v4 extensions to support platform-specific custom actions. For more information, see https://wixtoolset.org/docs/fourthree/#converting-custom-wixui-dialog-sets."))
1711
{
1698
- element.Attribute("Value").Value = value + "_$(sys.BUILDARCHSHORT)";
1712
+ element.Attribute("Value").Value = eventValue + "_$(sys.BUILDARCHSHORT)";
1713
}
1714
}
1715
@@ -1982,8 +1996,9 @@ namespace WixToolset.Converters
1996
}
1997
1998
var xScript = xCustomAction.Attribute("Script");
1999
+ var collector = new InnerContentCollector();
2000
1986
- if (xScript != null && TryGetInnerText(xCustomAction, out var scriptText, out var comments))
2001
+ if (xScript != null && collector.CollectInnerTextWithTrailingWhitespaceAndCommentsForScriptFile(xCustomAction, out string value))
2002
{
2003
if (this.OnInformation(ConverterTestType.InnerTextDeprecated, xCustomAction, "Using {0} element text is deprecated. Extract the text to a file and use the 'ScriptSourceFile' attribute to reference it.", xCustomAction.Name.LocalName))
2004
{
@@ -1992,12 +2007,12 @@ namespace WixToolset.Converters
2007
var ext = (xScript.Value == "jscript") ? ".js" : (xScript.Value == "vbscript") ? ".vbs" : ".txt";
2008
2009
var scriptFile = Path.Combine(scriptFolder, id + ext);
1995
- File.WriteAllText(scriptFile, scriptText);
2010
+ File.WriteAllText(scriptFile, value);
2011
2012
RemoveChildren(xCustomAction);
2013
xCustomAction.Add(new XAttribute("ScriptSourceFile", scriptFile));
2014
2000
- if (comments.Any())
2015
+ if (collector.Comments.Any())
2016
{
2017
var remainingNodes = xCustomAction.NodesAfterSelf().ToList();
2018
var replacementNodes = remainingNodes.Where(e => XmlNodeType.Text != e.NodeType);
@@ -2005,7 +2020,7 @@ namespace WixToolset.Converters
2020
{
2021
node.Remove();
2022
}
2008
- foreach (var comment in comments)
2023
+ foreach (var comment in collector.Comments)
2024
{
2025
xCustomAction.Add(comment);
2026
xCustomAction.Add("\n");
@@ -2144,24 +2159,23 @@ namespace WixToolset.Converters
2159
2160
private void ConvertInnerTextToAttribute(XElement element, string attributeName)
2161
{
2147
- if (TryGetInnerText(element, out var text, out var comments))
2162
+ var collector = new InnerContentCollector();
2163
+
2164
+ if (collector.CollectInnerTextAndCommentsForAttributeValue(element, out string value))
2165
{
2166
// If the target attribute already exists, error if we have anything more than whitespace.
2167
var attribute = element.Attribute(attributeName);
2168
if (attribute != null)
2169
{
2153
- if (!String.IsNullOrWhiteSpace(text))
2154
- {
2155
- this.OnError(ConverterTestType.InnerTextDeprecated, attribute, "Using {0} element text is deprecated. Remove the element's text and use only the '{1}' attribute. See the conversion FAQ for more information: https://wixtoolset.org/docs/fourthree/faqs/#converting-packages", element.Name.LocalName, attributeName);
2156
- }
2170
+ this.OnError(ConverterTestType.InnerTextDeprecated, attribute, "Using {0} element text is deprecated. Remove the element's text and use only the '{1}' attribute. See the conversion FAQ for more information: https://wixtoolset.org/docs/fourthree/faqs/#converting-packages", element.Name.LocalName, attributeName);
2171
}
2172
else if (this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the '{1}' attribute instead.", element.Name.LocalName, attributeName))
2173
{
2174
using (var lab = new ConversionLab(element))
2175
{
2176
lab.RemoveOrphanTextNodes();
2163
- element.Add(new XAttribute(attributeName, text));
2164
- lab.AddCommentsAsSiblings(comments);
2177
+ element.Add(new XAttribute(attributeName, value));
2178
+ lab.AddCommentsAsSiblings(collector.Comments);
2179
}
2180
}
2181
}
@@ -2854,42 +2868,6 @@ namespace WixToolset.Converters
2868
}
2869
}
2870
2857
- private static bool TryGetInnerText(XElement element, out string value, out List<XNode> comments)
2858
- {
2859
- return TryGetInnerText(element, out value, out comments, new List<XNode>());
2860
- }
2861
-
2862
- private static bool TryGetInnerText(XElement element, out string value, out List<XNode> comments, List<XNode> initialComments)
2863
- {
2864
- value = null;
2865
- comments = null;
2866
- var found = false;
2867
-
2868
- var nodes = element.Nodes().ToList();
2869
- comments = initialComments;
2870
- var nonCommentNodes = new List<XNode>();
2871
-
2872
- foreach (var node in nodes)
2873
- {
2874
- if (XmlNodeType.Comment == node.NodeType)
2875
- {
2876
- comments.Add(node);
2877
- }
2878
- else
2879
- {
2880
- nonCommentNodes.Add(node);
2881
- }
2882
- }
2883
-
2884
- if (nonCommentNodes.Any() && nonCommentNodes.All(e => e.NodeType == XmlNodeType.Text || e.NodeType == XmlNodeType.CDATA))
2885
- {
2886
- value = String.Join(String.Empty, nonCommentNodes.Cast<XText>().Select(TrimTextValue));
2887
- found = true;
2888
- }
2889
-
2890
- return found;
2891
- }
2892
-
2871
private static bool IsTextNode(XNode node, out XText text)
2872
{
2873
text = null;
@@ -2910,22 +2888,6 @@ namespace WixToolset.Converters
2888
}
2889
}
2890
2913
- private static string TrimTextValue(XText text)
2914
- {
2915
- var value = text.Value;
2916
-
2917
- if (String.IsNullOrEmpty(value))
2918
- {
2919
- return String.Empty;
2920
- }
2921
- else if (text.NodeType == XmlNodeType.CDATA && String.IsNullOrWhiteSpace(value))
2922
- {
2923
- return " ";
2924
- }
2925
-
2926
- return value.Trim();
2927
- }
2928
-
2891
private static void RemoveChildren(XElement element)
2892
{
2893
var nodes = element.Nodes().ToList();
@@ -2975,6 +2937,250 @@ namespace WixToolset.Converters
2937
return true;
2938
}
2939
2940
+ // This class encapsulates methods for extraving text and comments from XElements. Multiple calls can be made to the collection
2941
+ // methods to processs multiple XElements. The Comments property is used to extract the list of comments accumulated during the collection process.
2942
+ private class InnerContentCollector
2943
+ {
2944
+ public InnerContentCollector()
2945
+ {
2946
+ this.Comments = new List<XNode>();
2947
+ }
2948
+
2949
+ public List<XNode> Comments { get; private set; }
2950
+
2951
+ public bool CollectInnerTextAndCommentsForAttributeValue(XElement element, out string collectedText)
2952
+ {
2953
+ char[] whitespaceChars = { ' ', '\t', '\r', '\n' };
2954
+ var nodes = element.Nodes().ToList();
2955
+ var inWhitespace = false;
2956
+ var cDataFound = false;
2957
+ var sb = new StringBuilder();
2958
+
2959
+ foreach (var node in nodes)
2960
+ {
2961
+ if (XmlNodeType.Comment == node.NodeType)
2962
+ {
2963
+ this.Comments.Add(node);
2964
+ }
2965
+ else if (XmlNodeType.CDATA == node.NodeType || XmlNodeType.Text == node.NodeType)
2966
+ {
2967
+ var isCData = XmlNodeType.CDATA == node.NodeType;
2968
+
2969
+ if (isCData)
2970
+ {
2971
+ cDataFound = true;
2972
+ }
2973
+
2974
+ var text = node is XText xtext ? xtext.Value : String.Empty;
2975
+ var nodeSB = new StringBuilder();
2976
+
2977
+ foreach (var c in text)
2978
+ {
2979
+ char? emit = c;
2980
+
2981
+ // Replace contiguous whitespace with a single space.
2982
+ if (' ' == c || '\r' == c || '\n' == c || '\t' == c)
2983
+ {
2984
+ if (!inWhitespace)
2985
+ {
2986
+ inWhitespace = true;
2987
+ emit = ' ';
2988
+ }
2989
+ else
2990
+ {
2991
+ emit = null;
2992
+ }
2993
+ }
2994
+ else
2995
+ {
2996
+ inWhitespace = false;
2997
+ }
2998
+
2999
+ if (emit.HasValue)
3000
+ {
3001
+ nodeSB.Append(emit);
3002
+ }
3003
+ }
3004
+
3005
+ text = nodeSB.ToString().Trim(whitespaceChars);
3006
+ sb.Append(text);
3007
+ }
3008
+ }
3009
+ var found = false;
3010
+ collectedText = sb.ToString();
3011
+
3012
+ if (0 < collectedText.Length)
3013
+ {
3014
+ found = true;
3015
+ }
3016
+
3017
+ collectedText = collectedText.Trim(whitespaceChars);
3018
+
3019
+ if (cDataFound)
3020
+ {
3021
+ found = true;
3022
+
3023
+ if (0 == collectedText.Length)
3024
+ {
3025
+ collectedText = " ";
3026
+ }
3027
+ }
3028
+
3029
+ return found;
3030
+ }
3031
+
3032
+ public bool CollectInnerTextWithTrailingWhitespaceAndCommentsForAttributeValue(XElement element, out string collectedText)
3033
+ {
3034
+ char[] whitespaceChars = { ' ', '\t', '\r', '\n' };
3035
+ var nodes = element.Nodes().ToList();
3036
+ var inWhitespace = false;
3037
+ var cDataFound = false;
3038
+ var whitespaceFound = false;
3039
+ var sb = new StringBuilder();
3040
+
3041
+ foreach (var node in nodes)
3042
+ {
3043
+ if (XmlNodeType.Comment == node.NodeType)
3044
+ {
3045
+ this.Comments.Add(node);
3046
+ }
3047
+ else if (XmlNodeType.CDATA == node.NodeType || XmlNodeType.Text == node.NodeType)
3048
+ {
3049
+ var isCData = XmlNodeType.CDATA == node.NodeType;
3050
+
3051
+ if (isCData)
3052
+ {
3053
+ cDataFound = true;
3054
+ }
3055
+
3056
+ var text = node is XText xtext ? xtext.Value : String.Empty;
3057
+ var nodeSB = new StringBuilder();
3058
+
3059
+ foreach (var c in text)
3060
+ {
3061
+ char? emit = c;
3062
+
3063
+ // Replace contiguous whitespace with a single space.
3064
+ if (' ' == c || '\r' == c || '\n' == c || '\t' == c)
3065
+ {
3066
+ if (!inWhitespace)
3067
+ {
3068
+ inWhitespace = true;
3069
+ whitespaceFound = true;
3070
+ emit = ' ';
3071
+ }
3072
+ else
3073
+ {
3074
+ emit = null;
3075
+ }
3076
+ }
3077
+ else
3078
+ {
3079
+ inWhitespace = false;
3080
+ }
3081
+
3082
+ if (emit.HasValue)
3083
+ {
3084
+ nodeSB.Append(emit);
3085
+ }
3086
+ }
3087
+
3088
+ text = nodeSB.ToString();
3089
+
3090
+ if (0 < text.Length)
3091
+ {
3092
+ text = text.Trim(whitespaceChars);
3093
+
3094
+ if (0 == text.Length)
3095
+ {
3096
+ text = " ";
3097
+ }
3098
+ }
3099
+
3100
+ sb.Append(text);
3101
+ }
3102
+ }
3103
+ var found = false;
3104
+ collectedText = sb.ToString();
3105
+
3106
+ if (0 < collectedText.Length)
3107
+ {
3108
+ found = true;
3109
+ }
3110
+
3111
+ collectedText = collectedText.Trim(whitespaceChars);
3112
+
3113
+ if (whitespaceFound)
3114
+ {
3115
+ found = true;
3116
+ }
3117
+
3118
+ if (cDataFound)
3119
+ {
3120
+ found = true;
3121
+
3122
+ if (0 == collectedText.Length)
3123
+ {
3124
+ collectedText = " ";
3125
+ }
3126
+ }
3127
+
3128
+ return found;
3129
+ }
3130
+
3131
+ public bool CollectInnerTextWithTrailingWhitespaceAndCommentsForScriptFile(XElement element, out string collectedText)
3132
+ {
3133
+ var value = String.Empty;
3134
+ char[] whitespaceChars = { ' ', '\t', '\r', '\n' };
3135
+ var nodes = element.Nodes().ToList();
3136
+ var cDataFound = false;
3137
+ var sb = new StringBuilder();
3138
+
3139
+ foreach (var node in nodes)
3140
+ {
3141
+ if (XmlNodeType.Comment == node.NodeType)
3142
+ {
3143
+ this.Comments.Add(node);
3144
+ }
3145
+ else if (XmlNodeType.CDATA == node.NodeType || XmlNodeType.Text == node.NodeType)
3146
+ {
3147
+ var isCData = XmlNodeType.CDATA == node.NodeType;
3148
+
3149
+ if (isCData)
3150
+ {
3151
+ cDataFound = true;
3152
+ }
3153
+
3154
+ var text = node is XText xtext ? xtext.Value.Trim(whitespaceChars) : String.Empty;
3155
+ sb.Append(text);
3156
+ }
3157
+ }
3158
+
3159
+ var found = false;
3160
+
3161
+ collectedText = sb.ToString();
3162
+
3163
+ if (0 < collectedText.Length)
3164
+ {
3165
+ found = true;
3166
+ }
3167
+
3168
+ collectedText = collectedText.Trim(whitespaceChars);
3169
+
3170
+ if (cDataFound)
3171
+ {
3172
+ found = true;
3173
+
3174
+ if (0 == collectedText.Length)
3175
+ {
3176
+ collectedText = " ";
3177
+ }
3178
+ }
3179
+
3180
+ return found;
3181
+ }
3182
+ }
3183
+
3184
/// <summary>
3185
/// Converter test types. These are used to condition error messages down to warnings.
3186
/// </summary>
src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs
+331
-10
@@ -21,8 +21,8 @@ namespace WixToolsetTest.Converters
21
" <UI>",
22
" <Dialog Id='Dlg1'>",
23
" <Control Id='Control1'>",
24
- " <Condition Action='disable'>x=y</Condition>",
25
- " <Condition Action='hide'>a<>b</Condition>",
24
+ " <Condition Action='disable'>$(var.x)=$(var.y)</Condition>",
25
+ " <Condition Action='hide'>$(var.a)<>$(var.b)</Condition>",
26
" </Control>",
27
" </Dialog>",
28
" </UI>",
@@ -35,7 +35,7 @@ namespace WixToolsetTest.Converters
35
" <Fragment>",
36
" <UI>",
37
" <Dialog Id=\"Dlg1\">",
38
- " <Control Id=\"Control1\" DisableCondition=\"x=y\" HideCondition=\"a<>b\" />",
38
+ " <Control Id=\"Control1\" DisableCondition=\"$(var.x)=$(var.y)\" HideCondition=\"$(var.a)<>$(var.b)\" />",
39
" </Dialog>",
40
" </UI>",
41
" </Fragment>",
@@ -64,8 +64,8 @@ namespace WixToolsetTest.Converters
64
" <UI>",
65
" <Dialog Id='Dlg1'>",
66
" <Control Id='Control1'>",
67
- " <Condition Action='hide'>x=y</Condition>",
68
- " <Condition Action='hide'>a<>b</Condition>",
67
+ " <Condition Action='hide'>$(var.x)=$(var.y)</Condition>",
68
+ " <Condition Action='hide'>$(var.a)<>$(var.b)</Condition>",
69
" </Control>",
70
" </Dialog>",
71
" </UI>",
@@ -78,7 +78,7 @@ namespace WixToolsetTest.Converters
78
" <Fragment>",
79
" <UI>",
80
" <Dialog Id=\"Dlg1\">",
81
- " <Control Id=\"Control1\" HideCondition=\"(x=y) OR (a<>b)\" />",
81
+ " <Control Id=\"Control1\" HideCondition=\"($(var.x)=$(var.y)) OR ($(var.a)<>$(var.b))\" />",
82
" </Dialog>",
83
" </UI>",
84
" </Fragment>",
@@ -107,8 +107,8 @@ namespace WixToolsetTest.Converters
107
" <UI>",
108
" <Dialog Id='Dlg1'>",
109
" <Control Id='Control1'>",
110
- " <Condition Action='disable'><!-- Comment 1 -->x=y</Condition>",
111
- " <Condition Action='hide'><!-- Comment 2 -->a<>b</Condition>",
110
+ " <Condition Action='disable'><!-- Comment 1 -->$(var.x)=$(var.y)</Condition>",
111
+ " <Condition Action='hide'><!-- Comment 2 -->$(var.a)<>$(var.b)</Condition>",
112
" </Control>",
113
" </Dialog>",
114
" </UI>",
@@ -123,7 +123,91 @@ namespace WixToolsetTest.Converters
123
" <Dialog Id=\"Dlg1\">",
124
" <!-- Comment 1 -->",
125
" <!-- Comment 2 -->",
126
- " <Control Id=\"Control1\" DisableCondition=\"x=y\" HideCondition=\"a<>b\" />",
126
+ " <Control Id=\"Control1\" DisableCondition=\"$(var.x)=$(var.y)\" HideCondition=\"$(var.a)<>$(var.b)\" />",
127
+ " </Dialog>",
128
+ " </UI>",
129
+ " </Fragment>",
130
+ "</Wix>"
131
+ };
132
+
133
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
134
+
135
+ var messaging = new MockMessaging();
136
+ var converter = new WixConverter(messaging, 2, null, null);
137
+
138
+ var errors = converter.ConvertDocument(document);
139
+ Assert.Equal(4, errors);
140
+
141
+ var actualLines = UnformattedDocumentLines(document);
142
+ WixAssert.CompareLineByLine(expected, actualLines);
143
+ }
144
+
145
+ [Fact]
146
+ public void DemonstrateTheHandlingOfQuotedBackslashesInInnerText()
147
+ {
148
+ var parse = String.Join(Environment.NewLine,
149
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
150
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
151
+ " <Fragment>",
152
+ " <UI>",
153
+ " <Dialog Id='Dlg1'>",
154
+ " <Control Id='Control1'>",
155
+ " <Condition Action='hide'>\"\\\"=$(var.separator)</Condition>",
156
+ " </Control>",
157
+ " </Dialog>",
158
+ " </UI>",
159
+ " </Fragment>",
160
+ "</Wix>");
161
+
162
+ var expected = new[]
163
+ {
164
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
165
+ " <Fragment>",
166
+ " <UI>",
167
+ " <Dialog Id=\"Dlg1\">",
168
+ " <Control Id=\"Control1\" HideCondition=\""\\"=$(var.separator)\" />",
169
+ " </Dialog>",
170
+ " </UI>",
171
+ " </Fragment>",
172
+ "</Wix>"
173
+ };
174
+
175
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
176
+
177
+ var messaging = new MockMessaging();
178
+ var converter = new WixConverter(messaging, 2, null, null);
179
+
180
+ var errors = converter.ConvertDocument(document);
181
+
182
+ var actualLines = UnformattedDocumentLines(document);
183
+ WixAssert.CompareLineByLine(expected, actualLines);
184
+ }
185
+
186
+ [Fact]
187
+ public void FixControlConditionWithStringConstants()
188
+ {
189
+ var parse = String.Join(Environment.NewLine,
190
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
191
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
192
+ " <Fragment>",
193
+ " <UI>",
194
+ " <Dialog Id='Dlg1'>",
195
+ " <Control Id='Control1'>",
196
+ " <Condition Action='hide'>\"x\"=$(var.y)</Condition>",
197
+ " <Condition Action='hide'>\"x's\"=$(var.y)</Condition>",
198
+ " </Control>",
199
+ " </Dialog>",
200
+ " </UI>",
201
+ " </Fragment>",
202
+ "</Wix>");
203
+
204
+ var expected = new[]
205
+ {
206
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
207
+ " <Fragment>",
208
+ " <UI>",
209
+ " <Dialog Id=\"Dlg1\">",
210
+ " <Control Id=\"Control1\" HideCondition=\"("x"=$(var.y)) OR ("x's"=$(var.y))\" />",
211
" </Dialog>",
212
" </UI>",
213
" </Fragment>",
@@ -305,6 +389,78 @@ namespace WixToolsetTest.Converters
389
WixAssert.CompareLineByLine(expected, actualLines);
390
}
391
392
+ [Fact]
393
+ public void FixMultilineComponentCondition()
394
+ {
395
+ var parse = String.Join(Environment.NewLine,
396
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
397
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
398
+ " <Fragment>",
399
+ " <Component Id='Comp1' Directory='ApplicationFolder'>",
400
+ " <Condition>1<2</Condition>",
401
+ " </Component>",
402
+ " </Fragment>",
403
+ "</Wix>");
404
+
405
+ var expected = new[]
406
+ {
407
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
408
+ " <Fragment>",
409
+ " <Component Id=\"Comp1\" Directory=\"ApplicationFolder\" Condition=\"1<2\" />",
410
+ " </Fragment>",
411
+ "</Wix>"
412
+ };
413
+
414
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
415
+
416
+ var messaging = new MockMessaging();
417
+ var converter = new WixConverter(messaging, 2, null, null);
418
+
419
+ var errors = converter.ConvertDocument(document);
420
+ Assert.Equal(3, errors);
421
+
422
+ var actualLines = UnformattedDocumentLines(document);
423
+ WixAssert.CompareLineByLine(expected, actualLines);
424
+ }
425
+
426
+ [Fact]
427
+ public void FixMultilineComponentConditionWithComment()
428
+ {
429
+ var parse = String.Join(Environment.NewLine,
430
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
431
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
432
+ " <Fragment>",
433
+ " <Component Id='Comp1' Directory='ApplicationFolder'>",
434
+ " <Condition>",
435
+ " <!-- Comment -->",
436
+ " 1<2 OR ",
437
+ " 4<3",
438
+ " </Condition>",
439
+ " </Component>",
440
+ " </Fragment>",
441
+ "</Wix>");
442
+ var expected = new[]
443
+ {
444
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
445
+ " <Fragment>",
446
+ " <!-- Comment -->",
447
+ " <Component Id=\"Comp1\" Directory=\"ApplicationFolder\" Condition=\"1<2 OR 4<3\" />",
448
+ " </Fragment>",
449
+ "</Wix>"
450
+ };
451
+
452
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
453
+
454
+ var messaging = new MockMessaging();
455
+ var converter = new WixConverter(messaging, 2, null, null);
456
+
457
+ var errors = converter.ConvertDocument(document);
458
+ Assert.Equal(3, errors);
459
+
460
+ var actualLines = UnformattedDocumentLines(document);
461
+ WixAssert.CompareLineByLine(expected, actualLines);
462
+ }
463
+
464
[Fact]
465
public void FixEmptyCondition()
466
{
@@ -480,6 +636,90 @@ namespace WixToolsetTest.Converters
636
WixAssert.CompareLineByLine(expected, actualLines);
637
}
638
639
+ [Fact]
640
+ public void FixMultilineFeatureCondition()
641
+ {
642
+ var parse = String.Join(Environment.NewLine,
643
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
644
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
645
+ " <Fragment>",
646
+ " <Feature Id='ShellExtensionFeature' Level='0' InstallDefault='local' >",
647
+ " <Condition Level='1'>",
648
+ " (NOT Installed AND NOT PERUSER) OR",
649
+ " (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)",
650
+ " </Condition>",
651
+ " <ComponentGroupRef Id='ShellExtComponents' />",
652
+ " </Feature>",
653
+ " </Fragment>",
654
+ "</Wix>");
655
+
656
+ var expected = new[]
657
+ {
658
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
659
+ " <Fragment>",
660
+ " <Feature Id=\"ShellExtensionFeature\" Level=\"0\" InstallDefault=\"local\">",
661
+ " <Level Value=\"1\" Condition=\"(NOT Installed AND NOT PERUSER) OR (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)\" />",
662
+ " <ComponentGroupRef Id=\"ShellExtComponents\" />",
663
+ " </Feature>",
664
+ " </Fragment>",
665
+ "</Wix>"
666
+ };
667
+
668
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
669
+
670
+ var messaging = new MockMessaging();
671
+ var converter = new WixConverter(messaging, 2, null, null);
672
+
673
+ var errors = converter.ConvertDocument(document);
674
+ Assert.Equal(3, errors);
675
+
676
+ var actualLines = UnformattedDocumentLines(document);
677
+ WixAssert.CompareLineByLine(expected, actualLines);
678
+ }
679
+
680
+ [Fact]
681
+ public void FixMultilineFeatureConditionWithComment()
682
+ {
683
+ var parse = String.Join(Environment.NewLine,
684
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
685
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
686
+ " <Fragment>",
687
+ " <Feature Id='ShellExtensionFeature' Level='0' InstallDefault='local' >",
688
+ " <Condition Level='1'>",
689
+ " <!-- Comment -->",
690
+ " (NOT Installed AND NOT PERUSER) OR",
691
+ " (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)",
692
+ " </Condition>",
693
+ " <ComponentGroupRef Id='ShellExtComponents' />",
694
+ " </Feature>",
695
+ " </Fragment>",
696
+ "</Wix>");
697
+
698
+ var expected = new[]
699
+ {
700
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
701
+ " <Fragment>",
702
+ " <Feature Id=\"ShellExtensionFeature\" Level=\"0\" InstallDefault=\"local\">",
703
+ " <!-- Comment -->",
704
+ " <Level Value=\"1\" Condition=\"(NOT Installed AND NOT PERUSER) OR (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)\" />",
705
+ " <ComponentGroupRef Id=\"ShellExtComponents\" />",
706
+ " </Feature>",
707
+ " </Fragment>",
708
+ "</Wix>"
709
+ };
710
+
711
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
712
+
713
+ var messaging = new MockMessaging();
714
+ var converter = new WixConverter(messaging, 2, null, null);
715
+
716
+ var errors = converter.ConvertDocument(document);
717
+ Assert.Equal(3, errors);
718
+
719
+ var actualLines = UnformattedDocumentLines(document);
720
+ WixAssert.CompareLineByLine(expected, actualLines);
721
+ }
722
+
723
[Fact]
724
public void FixLaunchConditionInFragment()
725
{
@@ -519,7 +759,7 @@ namespace WixToolsetTest.Converters
759
}
760
761
[Fact]
522
- public void FixLaunchConditionWithComment()
762
+ public void FixLaunchConditionInFragmentWithComment()
763
{
764
var parse = String.Join(Environment.NewLine,
765
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
@@ -558,6 +798,87 @@ namespace WixToolsetTest.Converters
798
WixAssert.CompareLineByLine(expected, actualLines);
799
}
800
801
+ [Fact]
802
+ public void FixMultilineLaunchConditionInFragment()
803
+ {
804
+ var parse = String.Join(Environment.NewLine,
805
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
806
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
807
+ " <Fragment>",
808
+ " <Condition Message='Stop the install'>",
809
+ " 1<2 OR ",
810
+ " 4<3",
811
+ " </Condition>",
812
+ " <Condition Message='Do not stop'>",
813
+ " 1=2",
814
+ " </Condition>",
815
+ " </Fragment>",
816
+ "</Wix>");
817
+
818
+ var expected = new[]
819
+ {
820
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
821
+ " <Fragment>",
822
+ " <Launch Condition=\"1<2 OR 4<3\" Message=\"Stop the install\" />",
823
+ " <Launch Condition=\"1=2\" Message=\"Do not stop\" />",
824
+ " </Fragment>",
825
+ "</Wix>"
826
+ };
827
+
828
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
829
+
830
+ var messaging = new MockMessaging();
831
+ var converter = new WixConverter(messaging, 2, null, null);
832
+
833
+ var errors = converter.ConvertDocument(document);
834
+ Assert.Equal(4, errors);
835
+
836
+ var actualLines = UnformattedDocumentLines(document);
837
+ WixAssert.CompareLineByLine(expected, actualLines);
838
+ }
839
+
840
+ [Fact]
841
+ public void FixMultilineLaunchConditionInFragmentWithComment()
842
+ {
843
+ var parse = String.Join(Environment.NewLine,
844
+ "<?xml version=\"1.0\" encoding=\"utf-16\"?>",
845
+ "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
846
+ " <Fragment>",
847
+ " <Condition Message='Stop the install'>",
848
+ " <!-- Comment 1 -->",
849
+ " 1<2 OR",
850
+ " 4<3",
851
+ " </Condition>",
852
+ " <Condition Message='Do not stop'>",
853
+ " <!-- Comment 2 -->1=2",
854
+ " </Condition>",
855
+ " </Fragment>",
856
+ "</Wix>");
857
+
858
+ var expected = new[]
859
+ {
860
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
861
+ " <Fragment>",
862
+ " <!-- Comment 1 -->",
863
+ " <Launch Condition=\"1<2 OR 4<3\" Message=\"Stop the install\" />",
864
+ " <!-- Comment 2 -->",
865
+ " <Launch Condition=\"1=2\" Message=\"Do not stop\" />",
866
+ " </Fragment>",
867
+ "</Wix>"
868
+ };
869
+
870
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
871
+
872
+ var messaging = new MockMessaging();
873
+ var converter = new WixConverter(messaging, 2, null, null);
874
+
875
+ var errors = converter.ConvertDocument(document);
876
+ Assert.Equal(4, errors);
877
+
878
+ var actualLines = UnformattedDocumentLines(document);
879
+ WixAssert.CompareLineByLine(expected, actualLines);
880
+ }
881
+
882
[Fact]
883
public void FixLaunchConditionInProduct()
884
{