Fix resolution of localizations that are embedded in intermediates
Rob Mensching committed
Dec 30, 2017 at 17:09 UTC
c5190ae74ab8fe13609362efce88fa4b8cc24f34
12 files changed
+505
-480
src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+1
-1
@@ -96,7 +96,7 @@ namespace WixToolset.Core.Burn
96
97
public string IntermediateFolder { private get; set; }
98
99
- public IBindVariableResolver WixVariableResolver { private get; set; }
99
+ public IVariableResolver VariableResolver { private get; set; }
100
101
public IEnumerable<FileTransfer> FileTransfers { get; private set; }
102
src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
+73
-2
@@ -5,6 +5,7 @@ namespace WixToolset.Core.Bind
5
using System;
6
using System.Collections.Generic;
7
using System.Globalization;
8
+ using System.Text;
9
using WixToolset.Data;
10
using WixToolset.Extensibility;
11
using WixToolset.Extensibility.Services;
@@ -46,7 +47,7 @@ namespace WixToolset.Core.Bind
47
// process properties first in case they refer to other binder variables
48
if (delayedField.Row.Definition.Type == TupleDefinitionType.Property)
49
{
49
- var value = WixVariableResolver.ResolveDelayedVariables(propertyRow.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache);
50
+ var value = ResolveDelayedVariables(propertyRow.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache);
51
52
// update the variable cache with the new value
53
var key = String.Concat("property.", propertyRow.AsString(0));
@@ -102,7 +103,7 @@ namespace WixToolset.Core.Bind
103
{
104
try
105
{
105
- var value = WixVariableResolver.ResolveDelayedVariables(delayedField.Row.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache);
106
+ var value = ResolveDelayedVariables(delayedField.Row.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache);
107
delayedField.Field.Set(value);
108
}
109
catch (WixException we)
@@ -111,5 +112,75 @@ namespace WixToolset.Core.Bind
112
}
113
}
114
}
115
+
116
+ public static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary<string, string> resolutionData)
117
+ {
118
+ var matches = Common.WixVariableRegex.Matches(value);
119
+
120
+ if (0 < matches.Count)
121
+ {
122
+ var sb = new StringBuilder(value);
123
+
124
+ // notice how this code walks backward through the list
125
+ // because it modifies the string as we go through it
126
+ for (int i = matches.Count - 1; 0 <= i; i--)
127
+ {
128
+ string variableNamespace = matches[i].Groups["namespace"].Value;
129
+ string variableId = matches[i].Groups["fullname"].Value;
130
+ string variableDefaultValue = null;
131
+ string variableScope = null;
132
+
133
+ // get the default value if one was specified
134
+ if (matches[i].Groups["value"].Success)
135
+ {
136
+ variableDefaultValue = matches[i].Groups["value"].Value;
137
+ }
138
+
139
+ // get the scope if one was specified
140
+ if (matches[i].Groups["scope"].Success)
141
+ {
142
+ variableScope = matches[i].Groups["scope"].Value;
143
+ if ("bind" == variableNamespace)
144
+ {
145
+ variableId = matches[i].Groups["name"].Value;
146
+ }
147
+ }
148
+
149
+ // check for an escape sequence of !! indicating the match is not a variable expression
150
+ if (0 < matches[i].Index && '!' == sb[matches[i].Index - 1])
151
+ {
152
+ sb.Remove(matches[i].Index - 1, 1);
153
+ }
154
+ else
155
+ {
156
+ string key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", variableId, variableScope).ToLower(CultureInfo.InvariantCulture);
157
+ string resolvedValue = variableDefaultValue;
158
+
159
+ if (resolutionData.ContainsKey(key))
160
+ {
161
+ resolvedValue = resolutionData[key];
162
+ }
163
+
164
+ if ("bind" == variableNamespace)
165
+ {
166
+ // insert the resolved value if it was found or display an error
167
+ if (null != resolvedValue)
168
+ {
169
+ sb.Remove(matches[i].Index, matches[i].Length);
170
+ sb.Insert(matches[i].Index, resolvedValue);
171
+ }
172
+ else
173
+ {
174
+ throw new WixException(ErrorMessages.UnresolvedBindReference(sourceLineNumbers, value));
175
+ }
176
+ }
177
+ }
178
+ }
179
+
180
+ value = sb.ToString();
181
+ }
182
+
183
+ return value;
184
+ }
185
}
186
}
src/WixToolset.Core/Bind/ResolveFieldsCommand.cs
+2
-2
@@ -18,7 +18,7 @@ namespace WixToolset.Core.Bind
18
19
public bool BuildingPatch { private get; set; }
20
21
- public IBindVariableResolver BindVariableResolver { private get; set; }
21
+ public IVariableResolver VariableResolver { private get; set; }
22
23
public IEnumerable<BindPath> BindPaths { private get; set; }
24
@@ -62,7 +62,7 @@ namespace WixToolset.Core.Bind
62
var original = field.AsString();
63
if (!String.IsNullOrEmpty(original))
64
{
65
- var resolution = this.BindVariableResolver.ResolveVariables(row.SourceLineNumbers, original, false);
65
+ var resolution = this.VariableResolver.ResolveVariables(row.SourceLineNumbers, original, false);
66
if (resolution.UpdatedValue)
67
{
68
field.Set(resolution.Value);
src/WixToolset.Core/CommandLine/BuildCommand.cs
+3
-1
@@ -186,7 +186,9 @@ namespace WixToolset.Core.CommandLine
186
187
private void BindPhase(Intermediate output)
188
{
189
- var localizations = this.LoadLocalizationFiles().ToList();
189
+ var localizations = new List<Localization>(output.Localizations);
190
+
191
+ localizations.AddRange(this.LoadLocalizationFiles());
192
193
// If there was an error loading localization files, then bail.
194
if (this.Messaging.EncounteredError)
src/WixToolset.Core/Compiler.cs
+1
-1
@@ -2446,7 +2446,7 @@ namespace WixToolset.Core
2446
{
2447
if (isGeneratableGuidOk || keyFound && !String.IsNullOrEmpty(keyPath))
2448
{
2449
- this.componentIdPlaceholdersResolver.AddVariable(componentIdPlaceholder, keyPath, false);
2449
+ this.componentIdPlaceholdersResolver.AddVariable(sourceLineNumbers, componentIdPlaceholder, keyPath, false);
2450
2451
id = new Identifier(keyPath, AccessModifier.Private);
2452
}
src/WixToolset.Core/Librarian.cs
+25
-46
@@ -54,39 +54,46 @@ namespace WixToolset.Core
54
extension.PreCombine(this.Context);
55
}
56
57
- var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList();
58
-
59
- var embedFilePaths = this.ResolveFilePathsToEmbed(sections);
57
+ Intermediate library = null;
58
+ try
59
+ {
60
+ var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList();
61
61
- var localizationsByCulture = this.CollateLocalizations(this.Context.Localizations);
62
+ var collate = new CollateLocalizationsCommand(this.Context.Messaging, this.Context.Localizations);
63
+ var localizationsByCulture = collate.Execute();
64
63
- if (this.Context.Messaging.EncounteredError)
64
- {
65
- return null;
66
- }
65
+ if (this.Context.Messaging.EncounteredError)
66
+ {
67
+ return null;
68
+ }
69
68
- foreach (var section in sections)
69
- {
70
- section.LibraryId = this.Context.LibraryId;
71
- }
70
+ var embedFilePaths = this.ResolveFilePathsToEmbed(sections);
71
73
- var library = new Intermediate(this.Context.LibraryId, sections, localizationsByCulture, embedFilePaths);
72
+ foreach (var section in sections)
73
+ {
74
+ section.LibraryId = this.Context.LibraryId;
75
+ }
76
75
- this.Validate(library);
77
+ library = new Intermediate(this.Context.LibraryId, sections, localizationsByCulture, embedFilePaths);
78
77
- foreach (var extension in this.Context.Extensions)
79
+ this.Validate(library);
80
+ }
81
+ finally
82
{
79
- extension.PostCombine(library);
83
+ foreach (var extension in this.Context.Extensions)
84
+ {
85
+ extension.PostCombine(library);
86
+ }
87
}
88
82
- return library;
89
+ return this.Context.Messaging.EncounteredError ? null : library;
90
}
91
92
/// <summary>
93
/// Validate that a library contains one entry section and no duplicate symbols.
94
/// </summary>
95
/// <param name="library">Library to validate.</param>
89
- private Intermediate Validate(Intermediate library)
96
+ private void Validate(Intermediate library)
97
{
98
FindEntrySectionAndLoadSymbolsCommand find = new FindEntrySectionAndLoadSymbolsCommand(this.Context.Messaging, library.Sections);
99
find.Execute();
@@ -100,34 +107,6 @@ namespace WixToolset.Core
107
// ReportDuplicateResolvedSymbolErrorsCommand reportDupes = new ReportDuplicateResolvedSymbolErrorsCommand(find.SymbolsWithDuplicates, resolve.ResolvedSections);
108
// reportDupes.Execute();
109
// }
103
-
104
- return (this.Context.Messaging.EncounteredError ? null : library);
105
- }
106
-
107
- private Dictionary<string, Localization> CollateLocalizations(IEnumerable<Localization> localizations)
108
- {
109
- var localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase);
110
-
111
- foreach (var localization in localizations)
112
- {
113
- if (localizationsByCulture.TryGetValue(localization.Culture, out var existingCulture))
114
- {
115
- try
116
- {
117
- existingCulture.Merge(localization);
118
- }
119
- catch (WixException e)
120
- {
121
- this.Context.Messaging.Write(e.Error);
122
- }
123
- }
124
- else
125
- {
126
- localizationsByCulture.Add(localization.Culture, localization);
127
- }
128
- }
129
-
130
- return localizationsByCulture;
110
}
111
112
private List<string> ResolveFilePathsToEmbed(IEnumerable<IntermediateSection> sections)
src/WixToolset.Core/Link/CollateLocalizationsCommand.cs
new
+71
@@ -0,0 +1,71 @@
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.Link
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Linq;
8
+ using WixToolset.Data;
9
+ using WixToolset.Extensibility.Services;
10
+
11
+ internal class CollateLocalizationsCommand
12
+ {
13
+ public CollateLocalizationsCommand(IMessaging messaging, IEnumerable<Localization> localizations)
14
+ {
15
+ this.Messaging = messaging;
16
+ this.Localizations = localizations;
17
+ }
18
+
19
+ private IMessaging Messaging { get; }
20
+
21
+ private IEnumerable<Localization> Localizations { get; }
22
+
23
+ public Dictionary<string, Localization> Execute()
24
+ {
25
+ var localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase);
26
+
27
+ foreach (var localization in this.Localizations)
28
+ {
29
+ if (localizationsByCulture.TryGetValue(localization.Culture, out var existingCulture))
30
+ {
31
+ var merged = this.Merge(existingCulture, localization);
32
+ localizationsByCulture[localization.Culture] = merged;
33
+ }
34
+ else
35
+ {
36
+ localizationsByCulture.Add(localization.Culture, localization);
37
+ }
38
+ }
39
+
40
+ return localizationsByCulture;
41
+ }
42
+
43
+ private Localization Merge(Localization existingLocalization, Localization localization)
44
+ {
45
+ var variables = existingLocalization.Variables.ToDictionary(v => v.Id);
46
+ var controls = existingLocalization.LocalizedControls.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
47
+
48
+ foreach (var newVariable in localization.Variables)
49
+ {
50
+ if (!variables.TryGetValue(newVariable.Id, out var existingVariable) || (existingVariable.Overridable && !newVariable.Overridable))
51
+ {
52
+ variables[newVariable.Id] = newVariable;
53
+ }
54
+ else if (!newVariable.Overridable)
55
+ {
56
+ this.Messaging.Write(ErrorMessages.DuplicateLocalizationIdentifier(newVariable.SourceLineNumbers, newVariable.Id));
57
+ }
58
+ }
59
+
60
+ foreach (var localizedControl in localization.LocalizedControls)
61
+ {
62
+ if (!controls.ContainsKey(localizedControl.Key))
63
+ {
64
+ controls.Add(localizedControl.Key, localizedControl.Value);
65
+ }
66
+ }
67
+
68
+ return new Localization(existingLocalization.Codepage, existingLocalization.Culture, variables, controls);
69
+ }
70
+ }
71
+}
src/WixToolset.Core/Linker.cs
+220
-200
@@ -32,9 +32,6 @@ namespace WixToolset.Core
32
{
33
this.ServiceProvider = serviceProvider;
34
this.sectionIdOnRows = true; // TODO: what is the correct value for this?
35
-
36
- //this.extensionData = new List<IExtensionData>();
37
- //this.inspectorExtensions = new List<InspectorExtension>();
35
}
36
37
private IServiceProvider ServiceProvider { get; }
@@ -78,40 +75,49 @@ namespace WixToolset.Core
75
this.Context.Extensions = extensionManager.Create<ILinkerExtension>();
76
this.Context.ExtensionData = extensionManager.Create<IExtensionData>();
77
this.Context.ExpectedOutputType = this.OutputType;
81
- this.Context.Intermediates = this.Intermediates.Union(this.Libraries).ToList();
78
+ this.Context.Intermediates = this.Intermediates.Concat(this.Libraries).ToList();
79
this.Context.TupleDefinitionCreator = creator;
80
84
- var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList();
81
+ foreach (var extension in this.Context.Extensions)
82
+ {
83
+ extension.PreLink(this.Context);
84
+ }
85
86
- // Add sections from the extensions with data.
87
- foreach (var data in this.Context.ExtensionData)
86
+ Intermediate intermediate = null;
87
+ try
88
{
89
- var library = data.GetLibrary(this.Context.TupleDefinitionCreator);
89
+ var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList();
90
+ var localizations = this.Context.Intermediates.SelectMany(i => i.Localizations).ToList();
91
91
- if (library != null)
92
+ // Add sections from the extensions with data.
93
+ foreach (var data in this.Context.ExtensionData)
94
{
93
- sections.AddRange(library.Sections);
95
+ var library = data.GetLibrary(this.Context.TupleDefinitionCreator);
96
+
97
+ if (library != null)
98
+ {
99
+ sections.AddRange(library.Sections);
100
+ }
101
}
95
- }
102
103
#if MOVE_TO_BACKEND
104
bool containsModuleSubstitution = false;
105
bool containsModuleConfiguration = false;
106
#endif
107
102
- //this.activeOutput = null;
108
+ //this.activeOutput = null;
109
104
- //TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection();
105
- //IntermediateTuple customRows = new List<IntermediateTuple>();
110
+ //TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection();
111
+ //IntermediateTuple customRows = new List<IntermediateTuple>();
112
113
#if MOVE_TO_BACKEND
114
StringCollection generatedShortFileNameIdentifiers = new StringCollection();
115
Hashtable generatedShortFileNames = new Hashtable();
116
#endif
117
112
- Hashtable multipleFeatureComponents = new Hashtable();
118
+ Hashtable multipleFeatureComponents = new Hashtable();
119
114
- var wixVariables = new Dictionary<string, WixVariableTuple>();
120
+ var wixVariables = new Dictionary<string, WixVariableTuple>();
121
122
#if MOVE_TO_BACKEND
123
// verify that modularization types match for foreign key relationships
@@ -143,114 +149,117 @@ namespace WixToolset.Core
149
}
150
#endif
151
146
- // First find the entry section and while processing all sections load all the symbols from all of the sections.
147
- // sections.FindEntrySectionAndLoadSymbols(false, this, expectedOutputType, out entrySection, out allSymbols);
148
- var find = new FindEntrySectionAndLoadSymbolsCommand(this.Context.Messaging, sections);
149
- find.ExpectedOutputType = this.Context.ExpectedOutputType;
150
- find.Execute();
152
+ // First find the entry section and while processing all sections load all the symbols from all of the sections.
153
+ // sections.FindEntrySectionAndLoadSymbols(false, this, expectedOutputType, out entrySection, out allSymbols);
154
+ var find = new FindEntrySectionAndLoadSymbolsCommand(this.Context.Messaging, sections);
155
+ find.ExpectedOutputType = this.Context.ExpectedOutputType;
156
+ find.Execute();
157
152
- // Must have found the entry section by now.
153
- if (null == find.EntrySection)
154
- {
155
- throw new WixException(ErrorMessages.MissingEntrySection(this.Context.ExpectedOutputType.ToString()));
156
- }
158
+ // Must have found the entry section by now.
159
+ if (null == find.EntrySection)
160
+ {
161
+ throw new WixException(ErrorMessages.MissingEntrySection(this.Context.ExpectedOutputType.ToString()));
162
+ }
163
158
- // Add the missing standard action symbols.
159
- this.LoadStandardActionSymbols(find.EntrySection, find.Symbols);
164
+ // Add the missing standard action symbols.
165
+ this.LoadStandardActionSymbols(find.EntrySection, find.Symbols);
166
161
- // Resolve the symbol references to find the set of sections we care about for linking.
162
- // Of course, we start with the entry section (that's how it got its name after all).
163
- var resolve = new ResolveReferencesCommand(this.Context.Messaging, find.EntrySection, find.Symbols);
164
- resolve.BuildingMergeModule = (SectionType.Module == find.EntrySection.Type);
167
+ // Resolve the symbol references to find the set of sections we care about for linking.
168
+ // Of course, we start with the entry section (that's how it got its name after all).
169
+ var resolve = new ResolveReferencesCommand(this.Context.Messaging, find.EntrySection, find.Symbols);
170
+ resolve.BuildingMergeModule = (SectionType.Module == find.EntrySection.Type);
171
166
- resolve.Execute();
172
+ resolve.Execute();
173
168
- if (this.Context.Messaging.EncounteredError)
169
- {
170
- return null;
171
- }
174
+ if (this.Context.Messaging.EncounteredError)
175
+ {
176
+ return null;
177
+ }
178
173
- // Reset the sections to only those that were resolved then flatten the complex
174
- // references that particpate in groups.
175
- sections = resolve.ResolvedSections.ToList();
179
+ // Reset the sections to only those that were resolved then flatten the complex
180
+ // references that particpate in groups.
181
+ sections = resolve.ResolvedSections.ToList();
182
177
- this.FlattenSectionsComplexReferences(sections);
183
+ // TODO: consider filtering "localizations" down to only those localizations from
184
+ // intermediates in the sections.
185
179
- if (this.Context.Messaging.EncounteredError)
180
- {
181
- return null;
182
- }
186
+ this.FlattenSectionsComplexReferences(sections);
187
184
- // The hard part in linking is processing the complex references.
185
- var referencedComponents = new HashSet<string>();
186
- var componentsToFeatures = new ConnectToFeatureCollection();
187
- var featuresToFeatures = new ConnectToFeatureCollection();
188
- var modulesToFeatures = new ConnectToFeatureCollection();
189
- this.ProcessComplexReferences(find.EntrySection, sections, referencedComponents, componentsToFeatures, featuresToFeatures, modulesToFeatures);
188
+ if (this.Context.Messaging.EncounteredError)
189
+ {
190
+ return null;
191
+ }
192
191
- if (this.Context.Messaging.EncounteredError)
192
- {
193
- return null;
194
- }
193
+ // The hard part in linking is processing the complex references.
194
+ var referencedComponents = new HashSet<string>();
195
+ var componentsToFeatures = new ConnectToFeatureCollection();
196
+ var featuresToFeatures = new ConnectToFeatureCollection();
197
+ var modulesToFeatures = new ConnectToFeatureCollection();
198
+ this.ProcessComplexReferences(find.EntrySection, sections, referencedComponents, componentsToFeatures, featuresToFeatures, modulesToFeatures);
199
196
- // Display an error message for Components that were not referenced by a Feature.
197
- foreach (var symbol in resolve.ReferencedSymbols.Where(s => s.Row.Definition.Type == TupleDefinitionType.Component))
198
- {
199
- if (!referencedComponents.Contains(symbol.Name))
200
+ if (this.Context.Messaging.EncounteredError)
201
{
201
- this.OnMessage(ErrorMessages.OrphanedComponent(symbol.Row.SourceLineNumbers, symbol.Row.Id.Id));
202
+ return null;
203
}
203
- }
204
205
- // Report duplicates that would ultimately end up being primary key collisions.
206
- var reportDupes = new ReportConflictingSymbolsCommand(this.Context.Messaging, find.PossiblyConflictingSymbols, resolve.ResolvedSections);
207
- reportDupes.Execute();
205
+ // Display an error message for Components that were not referenced by a Feature.
206
+ foreach (var symbol in resolve.ReferencedSymbols.Where(s => s.Row.Definition.Type == TupleDefinitionType.Component))
207
+ {
208
+ if (!referencedComponents.Contains(symbol.Name))
209
+ {
210
+ this.OnMessage(ErrorMessages.OrphanedComponent(symbol.Row.SourceLineNumbers, symbol.Row.Id.Id));
211
+ }
212
+ }
213
209
- if (this.Context.Messaging.EncounteredError)
210
- {
211
- return null;
212
- }
214
+ // Report duplicates that would ultimately end up being primary key collisions.
215
+ var reportDupes = new ReportConflictingSymbolsCommand(this.Context.Messaging, find.PossiblyConflictingSymbols, resolve.ResolvedSections);
216
+ reportDupes.Execute();
217
214
- // resolve the feature to feature connects
215
- this.ResolveFeatureToFeatureConnects(featuresToFeatures, find.Symbols);
218
+ if (this.Context.Messaging.EncounteredError)
219
+ {
220
+ return null;
221
+ }
222
217
- // start generating OutputTables and OutputRows for all the sections in the output
218
- var ensureTableRows = new List<IntermediateTuple>();
223
+ // resolve the feature to feature connects
224
+ this.ResolveFeatureToFeatureConnects(featuresToFeatures, find.Symbols);
225
220
- // Create the section to hold the linked content.
221
- var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type, find.EntrySection.Codepage);
226
+ // start generating OutputTables and OutputRows for all the sections in the output
227
+ var ensureTableRows = new List<IntermediateTuple>();
228
223
- var sectionCount = 0;
229
+ // Create the section to hold the linked content.
230
+ var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type, find.EntrySection.Codepage);
231
225
- foreach (var section in sections)
226
- {
227
- sectionCount++;
232
+ var sectionCount = 0;
233
229
- var sectionId = section.Id;
230
- if (null == sectionId && this.sectionIdOnRows)
234
+ foreach (var section in sections)
235
{
232
- sectionId = "wix.section." + sectionCount.ToString(CultureInfo.InvariantCulture);
233
- }
236
+ sectionCount++;
237
235
- foreach (var tuple in section.Tuples)
236
- {
237
- var copyTuple = true; // by default, copy tuples.
238
+ var sectionId = section.Id;
239
+ if (null == sectionId && this.sectionIdOnRows)
240
+ {
241
+ sectionId = "wix.section." + sectionCount.ToString(CultureInfo.InvariantCulture);
242
+ }
243
239
- // handle special tables
240
- switch (tuple.Definition.Type)
244
+ foreach (var tuple in section.Tuples)
245
{
246
+ var copyTuple = true; // by default, copy tuples.
247
+
248
+ // handle special tables
249
+ switch (tuple.Definition.Type)
250
+ {
251
#if MOVE_TO_BACKEND
252
case "AppSearch":
253
this.activeOutput.EnsureTable(this.tableDefinitions["Signature"]);
254
break;
255
#endif
256
248
- case TupleDefinitionType.Class:
249
- if (SectionType.Product == resolvedSection.Type)
250
- {
251
- this.ResolveFeatures(tuple, 2, 11, componentsToFeatures, multipleFeatureComponents);
252
- }
253
- break;
257
+ case TupleDefinitionType.Class:
258
+ if (SectionType.Product == resolvedSection.Type)
259
+ {
260
+ this.ResolveFeatures(tuple, 2, 11, componentsToFeatures, multipleFeatureComponents);
261
+ }
262
+ break;
263
264
#if MOVE_TO_BACKEND
265
case "CustomAction":
@@ -304,12 +313,12 @@ namespace WixToolset.Core
313
}
314
break;
315
#endif
307
- case TupleDefinitionType.Extension:
308
- if (SectionType.Product == resolvedSection.Type)
309
- {
310
- this.ResolveFeatures(tuple, 1, 4, componentsToFeatures, multipleFeatureComponents);
311
- }
312
- break;
316
+ case TupleDefinitionType.Extension:
317
+ if (SectionType.Product == resolvedSection.Type)
318
+ {
319
+ this.ResolveFeatures(tuple, 1, 4, componentsToFeatures, multipleFeatureComponents);
320
+ }
321
+ break;
322
323
#if MOVE_TO_BACKEND
324
case TupleDefinitionType.ModuleSubstitution:
@@ -321,12 +330,12 @@ namespace WixToolset.Core
330
break;
331
#endif
332
324
- case TupleDefinitionType.MsiAssembly:
325
- if (SectionType.Product == resolvedSection.Type)
326
- {
327
- this.ResolveFeatures(tuple, 0, 1, componentsToFeatures, multipleFeatureComponents);
328
- }
329
- break;
333
+ case TupleDefinitionType.MsiAssembly:
334
+ if (SectionType.Product == resolvedSection.Type)
335
+ {
336
+ this.ResolveFeatures(tuple, 0, 1, componentsToFeatures, multipleFeatureComponents);
337
+ }
338
+ break;
339
340
#if MOVE_TO_BACKEND
341
case "ProgId":
@@ -348,26 +357,26 @@ namespace WixToolset.Core
357
break;
358
#endif
359
351
- case TupleDefinitionType.PublishComponent:
352
- if (SectionType.Product == resolvedSection.Type)
353
- {
354
- this.ResolveFeatures(tuple, 2, 4, componentsToFeatures, multipleFeatureComponents);
355
- }
356
- break;
360
+ case TupleDefinitionType.PublishComponent:
361
+ if (SectionType.Product == resolvedSection.Type)
362
+ {
363
+ this.ResolveFeatures(tuple, 2, 4, componentsToFeatures, multipleFeatureComponents);
364
+ }
365
+ break;
366
358
- case TupleDefinitionType.Shortcut:
359
- if (SectionType.Product == resolvedSection.Type)
360
- {
361
- this.ResolveFeatures(tuple, 3, 4, componentsToFeatures, multipleFeatureComponents);
362
- }
363
- break;
367
+ case TupleDefinitionType.Shortcut:
368
+ if (SectionType.Product == resolvedSection.Type)
369
+ {
370
+ this.ResolveFeatures(tuple, 3, 4, componentsToFeatures, multipleFeatureComponents);
371
+ }
372
+ break;
373
365
- case TupleDefinitionType.TypeLib:
366
- if (SectionType.Product == resolvedSection.Type)
367
- {
368
- this.ResolveFeatures(tuple, 2, 6, componentsToFeatures, multipleFeatureComponents);
369
- }
370
- break;
374
+ case TupleDefinitionType.TypeLib:
375
+ if (SectionType.Product == resolvedSection.Type)
376
+ {
377
+ this.ResolveFeatures(tuple, 2, 6, componentsToFeatures, multipleFeatureComponents);
378
+ }
379
+ break;
380
381
#if SOLVE_CUSTOM_TABLE
382
case "WixCustomTable":
@@ -385,9 +394,9 @@ namespace WixToolset.Core
394
break;
395
#endif
396
388
- case TupleDefinitionType.WixEnsureTable:
389
- ensureTableRows.Add(tuple);
390
- break;
397
+ case TupleDefinitionType.WixEnsureTable:
398
+ ensureTableRows.Add(tuple);
399
+ break;
400
401
402
#if MOVE_TO_BACKEND
@@ -410,66 +419,66 @@ namespace WixToolset.Core
419
break;
420
#endif
421
413
- case TupleDefinitionType.WixMerge:
414
- if (SectionType.Product == resolvedSection.Type)
415
- {
416
- this.ResolveFeatures(tuple, 0, 7, modulesToFeatures, null);
417
- }
418
- break;
419
-
420
- case TupleDefinitionType.WixComplexReference:
421
- copyTuple = false;
422
- break;
422
+ case TupleDefinitionType.WixMerge:
423
+ if (SectionType.Product == resolvedSection.Type)
424
+ {
425
+ this.ResolveFeatures(tuple, 0, 7, modulesToFeatures, null);
426
+ }
427
+ break;
428
424
- case TupleDefinitionType.WixSimpleReference:
425
- copyTuple = false;
426
- break;
429
+ case TupleDefinitionType.WixComplexReference:
430
+ copyTuple = false;
431
+ break;
432
428
- case TupleDefinitionType.WixVariable:
429
- // check for colliding values and collect the wix variable rows
430
- {
431
- var row = (WixVariableTuple)tuple;
433
+ case TupleDefinitionType.WixSimpleReference:
434
+ copyTuple = false;
435
+ break;
436
433
- if (wixVariables.TryGetValue(row.WixVariable, out var collidingRow))
437
+ case TupleDefinitionType.WixVariable:
438
+ // check for colliding values and collect the wix variable rows
439
{
435
- if (collidingRow.Overridable && !row.Overridable)
440
+ var row = (WixVariableTuple)tuple;
441
+
442
+ if (wixVariables.TryGetValue(row.WixVariable, out var collidingRow))
443
{
437
- wixVariables[row.WixVariable] = row;
444
+ if (collidingRow.Overridable && !row.Overridable)
445
+ {
446
+ wixVariables[row.WixVariable] = row;
447
+ }
448
+ else if (!row.Overridable || (collidingRow.Overridable && row.Overridable))
449
+ {
450
+ this.OnMessage(ErrorMessages.WixVariableCollision(row.SourceLineNumbers, row.WixVariable));
451
+ }
452
}
439
- else if (!row.Overridable || (collidingRow.Overridable && row.Overridable))
453
+ else
454
{
441
- this.OnMessage(ErrorMessages.WixVariableCollision(row.SourceLineNumbers, row.WixVariable));
455
+ wixVariables.Add(row.WixVariable, row);
456
}
457
}
444
- else
445
- {
446
- wixVariables.Add(row.WixVariable, row);
447
- }
448
- }
458
450
- copyTuple = false;
451
- break;
452
- }
459
+ copyTuple = false;
460
+ break;
461
+ }
462
454
- if (copyTuple)
455
- {
456
- resolvedSection.Tuples.Add(tuple);
463
+ if (copyTuple)
464
+ {
465
+ resolvedSection.Tuples.Add(tuple);
466
+ }
467
}
468
}
459
- }
469
461
- // copy the module to feature connections into the output
462
- foreach (ConnectToFeature connectToFeature in modulesToFeatures)
463
- {
464
- foreach (var feature in connectToFeature.ConnectFeatures)
470
+ // copy the module to feature connections into the output
471
+ foreach (ConnectToFeature connectToFeature in modulesToFeatures)
472
{
466
- var row = new WixFeatureModulesTuple();
467
- row.Feature_ = feature;
468
- row.WixMerge_ = connectToFeature.ChildId;
473
+ foreach (var feature in connectToFeature.ConnectFeatures)
474
+ {
475
+ var row = new WixFeatureModulesTuple();
476
+ row.Feature_ = feature;
477
+ row.WixMerge_ = connectToFeature.ChildId;
478
470
- resolvedSection.Tuples.Add(row);
479
+ resolvedSection.Tuples.Add(row);
480
+ }
481
}
472
- }
482
483
#if MOVE_TO_BACKEND
484
// ensure the creation of tables that need to exist
@@ -612,24 +621,24 @@ namespace WixToolset.Core
621
}
622
#endif
623
615
- //correct the section Id in FeatureComponents table
616
- if (this.sectionIdOnRows)
617
- {
618
- //var componentSectionIds = new Dictionary<string, string>();
619
-
620
- //foreach (var componentTuple in entrySection.Tuples.OfType<ComponentTuple>())
621
- //{
622
- // componentSectionIds.Add(componentTuple.Id.Id, componentTuple.SectionId);
623
- //}
624
-
625
- //foreach (var featureComponentTuple in entrySection.Tuples.OfType<FeatureComponentsTuple>())
626
- //{
627
- // if (componentSectionIds.TryGetValue(featureComponentTuple.Component_, out var componentSectionId))
628
- // {
629
- // featureComponentTuple.SectionId = componentSectionId;
630
- // }
631
- //}
632
- }
624
+ //correct the section Id in FeatureComponents table
625
+ if (this.sectionIdOnRows)
626
+ {
627
+ //var componentSectionIds = new Dictionary<string, string>();
628
+
629
+ //foreach (var componentTuple in entrySection.Tuples.OfType<ComponentTuple>())
630
+ //{
631
+ // componentSectionIds.Add(componentTuple.Id.Id, componentTuple.SectionId);
632
+ //}
633
+
634
+ //foreach (var featureComponentTuple in entrySection.Tuples.OfType<FeatureComponentsTuple>())
635
+ //{
636
+ // if (componentSectionIds.TryGetValue(featureComponentTuple.Component_, out var componentSectionId))
637
+ // {
638
+ // featureComponentTuple.SectionId = componentSectionId;
639
+ // }
640
+ //}
641
+ }
642
643
#if MOVE_TO_BACKEND
644
// add the ModuleSubstitution table to the ModuleIgnoreTable
@@ -698,27 +707,38 @@ namespace WixToolset.Core
707
}
708
#endif
709
701
- // copy the wix variable rows to the output after all overriding has been accounted for.
702
- foreach (var row in wixVariables.Values)
703
- {
704
- resolvedSection.Tuples.Add(row);
705
- }
710
+ // copy the wix variable rows to the output after all overriding has been accounted for.
711
+ foreach (var row in wixVariables.Values)
712
+ {
713
+ resolvedSection.Tuples.Add(row);
714
+ }
715
707
- // Bundles have groups of data that must be flattened in a way different from other types.
708
- this.FlattenBundleTables(resolvedSection);
716
+ // Bundles have groups of data that must be flattened in a way different from other types.
717
+ this.FlattenBundleTables(resolvedSection);
718
710
- if (this.Context.Messaging.EncounteredError)
711
- {
712
- return null;
713
- }
719
+ if (this.Context.Messaging.EncounteredError)
720
+ {
721
+ return null;
722
+ }
723
715
- var output = new Intermediate(resolvedSection.Id, new[] { resolvedSection }, null, null);
724
+ var collate = new CollateLocalizationsCommand(this.Context.Messaging, localizations);
725
+ var localizationsByCulture = collate.Execute();
726
+
727
+ intermediate = new Intermediate(resolvedSection.Id, new[] { resolvedSection }, localizationsByCulture, null);
728
729
#if MOVE_TO_BACKEND
730
this.CheckOutputConsistency(output);
731
#endif
732
+ }
733
+ finally
734
+ {
735
+ foreach (var extension in this.Context.Extensions)
736
+ {
737
+ extension.PostLink(intermediate);
738
+ }
739
+ }
740
721
- return this.Context.Messaging.EncounteredError ? null : output;
741
+ return this.Context.Messaging.EncounteredError ? null : intermediate;
742
}
743
744
#if SOLVE_CUSTOM_TABLE
src/WixToolset.Core/Localizer.cs
+1
-62
@@ -14,72 +14,11 @@ namespace WixToolset.Core
14
/// <summary>
15
/// Parses localization files and localizes database values.
16
/// </summary>
17
- public sealed class Localizer : ILocalizer
17
+ public sealed class Localizer
18
{
19
public static readonly XNamespace WxlNamespace = "http://wixtoolset.org/schemas/v4/wxl";
20
private static string XmlElementName = "WixLocalization";
21
22
- private Dictionary<string, BindVariable> variables;
23
- private Dictionary<string, LocalizedControl> localizedControls;
24
-
25
- /// <summary>
26
- /// Instantiate a new Localizer.
27
- /// </summary>
28
- public Localizer(IMessaging messaging, IEnumerable<Localization> localizations)
29
- {
30
- this.Codepage = -1;
31
- this.variables = new Dictionary<string, BindVariable>();
32
- this.localizedControls = new Dictionary<string, LocalizedControl>();
33
-
34
- foreach (var localization in localizations)
35
- {
36
- if (-1 == this.Codepage)
37
- {
38
- this.Codepage = localization.Codepage;
39
- }
40
-
41
- foreach (var variable in localization.Variables)
42
- {
43
- Localizer.AddWixVariable(messaging, this.variables, variable);
44
- }
45
-
46
- foreach (KeyValuePair<string, LocalizedControl> localizedControl in localization.LocalizedControls)
47
- {
48
- if (!this.localizedControls.ContainsKey(localizedControl.Key))
49
- {
50
- this.localizedControls.Add(localizedControl.Key, localizedControl.Value);
51
- }
52
- }
53
- }
54
- }
55
-
56
- /// <summary>
57
- /// Gets the codepage.
58
- /// </summary>
59
- /// <value>The codepage.</value>
60
- public int Codepage { get; }
61
-
62
- /// <summary>
63
- /// Get a localized data value.
64
- /// </summary>
65
- /// <param name="id">The name of the localization variable.</param>
66
- /// <returns>The localized data value or null if it wasn't found.</returns>
67
- public string GetLocalizedValue(string id)
68
- {
69
- return this.variables.TryGetValue(id, out var wixVariableRow) ? wixVariableRow.Value : null;
70
- }
71
-
72
- /// <summary>
73
- /// Get a localized control.
74
- /// </summary>
75
- /// <param name="dialog">The optional id of the control's dialog.</param>
76
- /// <param name="control">The id of the control.</param>
77
- /// <returns>The localized control or null if it wasn't found.</returns>
78
- public LocalizedControl GetLocalizedControl(string dialog, string control)
79
- {
80
- return this.localizedControls.TryGetValue(LocalizedControl.GetKey(dialog, control), out var localizedControl) ? localizedControl : null;
81
- }
82
-
22
/// <summary>
23
/// Loads a localization file from a path on disk.
24
/// </summary>
src/WixToolset.Core/ResolveContext.cs
+5
-1
@@ -23,10 +23,14 @@ namespace WixToolset.Core
23
24
public IEnumerable<IResolverExtension> Extensions { get; set; }
25
26
+ public IEnumerable<IExtensionData> ExtensionData { get; set; }
27
+
28
public string IntermediateFolder { get; set; }
29
30
public Intermediate IntermediateRepresentation { get; set; }
31
30
- public IBindVariableResolver WixVariableResolver { get; set; }
32
+ public IEnumerable<Localization> Localizations { get; set; }
33
+
34
+ public IVariableResolver VariableResolver { get; set; }
35
}
36
}
src/WixToolset.Core/Resolver.cs
+44
-37
@@ -25,48 +25,42 @@ namespace WixToolset.Core
25
26
public IEnumerable<BindPath> BindPaths { get; set; }
27
28
- public Intermediate IntermediateRepresentation { get; set; }
29
-
28
public string IntermediateFolder { get; set; }
29
32
- public IEnumerable<Localization> Localizations { get; set; }
30
+ public Intermediate IntermediateRepresentation { get; set; }
31
34
- private IMessaging Messaging { get; set; }
32
+ public IEnumerable<Localization> Localizations { get; set; }
33
34
public ResolveResult Execute()
35
{
38
- this.Messaging = this.ServiceProvider.GetService<IMessaging>();
39
-
40
- var localizer = new Localizer(this.Messaging, this.Localizations);
41
-
42
- var variableResolver = new WixVariableResolver(this.Messaging, localizer);
43
- this.PopulateVariableResolver(variableResolver);
36
+ var extensionManager = this.ServiceProvider.GetService<IExtensionManager>();
37
38
var context = this.ServiceProvider.GetService<IResolveContext>();
46
- context.Messaging = this.Messaging;
39
+ context.Messaging = this.ServiceProvider.GetService<IMessaging>();
40
context.BindPaths = this.BindPaths;
48
- context.Extensions = this.ServiceProvider.GetService<IExtensionManager>().Create<IResolverExtension>();
41
+ context.Extensions = extensionManager.Create<IResolverExtension>();
42
+ context.ExtensionData = extensionManager.Create<IExtensionData>();
43
context.IntermediateFolder = this.IntermediateFolder;
44
context.IntermediateRepresentation = this.IntermediateRepresentation;
51
- context.WixVariableResolver = variableResolver;
45
+ context.Localizations = this.Localizations;
46
+ context.VariableResolver = new WixVariableResolver(context.Messaging);
47
53
- // Preresolve.
54
- //
48
foreach (IResolverExtension extension in context.Extensions)
49
{
50
extension.PreResolve(context);
51
}
52
60
- // Resolve.
61
- //
62
- this.LocalizeUI(context);
53
+ ResolveResult resolveResult = null;
54
+ try
55
+ {
56
+ PopulateVariableResolver(context);
57
64
- var resolveResult = this.Resolve(localizer.Codepage, context);
58
+ this.LocalizeUI(context);
59
66
- if (resolveResult != null)
60
+ resolveResult = this.Resolve(context);
61
+ }
62
+ finally
63
{
68
- // Postresolve.
69
- //
64
foreach (IResolverExtension extension in context.Extensions)
65
{
66
extension.PostResolve(resolveResult);
@@ -76,7 +70,7 @@ namespace WixToolset.Core
70
return resolveResult;
71
}
72
79
- private ResolveResult Resolve(int codepage, IResolveContext context)
73
+ private ResolveResult Resolve(IResolveContext context)
74
{
75
var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch);
76
@@ -87,7 +81,7 @@ namespace WixToolset.Core
81
var command = new ResolveFieldsCommand();
82
command.Messaging = context.Messaging;
83
command.BuildingPatch = buildingPatch;
90
- command.BindVariableResolver = context.WixVariableResolver;
84
+ command.VariableResolver = context.VariableResolver;
85
command.BindPaths = context.BindPaths;
86
command.Extensions = context.Extensions;
87
command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
@@ -122,7 +116,7 @@ namespace WixToolset.Core
116
117
return new ResolveResult
118
{
125
- Codepage = codepage,
119
+ Codepage = context.VariableResolver.Codepage,
120
ExpectedEmbeddedFiles = expectedEmbeddedFiles,
121
DelayedFields = delayedFields,
122
IntermediateRepresentation = context.IntermediateRepresentation
@@ -140,7 +134,7 @@ namespace WixToolset.Core
134
{
135
string dialog = row.Dialog;
136
143
- if (context.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl))
137
+ if (context.VariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl))
138
{
139
if (CompilerConstants.IntegerNotSet != localizedControl.X)
140
{
@@ -176,7 +170,7 @@ namespace WixToolset.Core
170
string dialog = row.Dialog_;
171
string control = row.Control;
172
179
- if (context.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl))
173
+ if (context.VariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl))
174
{
175
if (CompilerConstants.IntegerNotSet != localizedControl.X)
176
{
@@ -209,21 +203,34 @@ namespace WixToolset.Core
203
}
204
}
205
212
- private void PopulateVariableResolver(WixVariableResolver resolver)
206
+ private static void PopulateVariableResolver(IResolveContext context)
207
{
214
- // Gather all the wix variables.
215
- var wixVariableTuples = this.IntermediateRepresentation.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>();
216
- foreach (var tuple in wixVariableTuples)
208
+ var creator = context.ServiceProvider.GetService<ITupleDefinitionCreator>();
209
+
210
+ var localizations = context.Localizations.Concat(context.IntermediateRepresentation.Localizations).ToList();
211
+
212
+ // Add localizations from the extensions with data.
213
+ foreach (var data in context.ExtensionData)
214
{
218
- try
219
- {
220
- resolver.AddVariable(tuple.WixVariable, tuple.Value, tuple.Overridable);
221
- }
222
- catch (ArgumentException)
215
+ var library = data.GetLibrary(creator);
216
+
217
+ if (library?.Localizations != null)
218
{
224
- this.Messaging.Write(ErrorMessages.WixVariableCollision(tuple.SourceLineNumbers, tuple.WixVariable));
219
+ localizations.AddRange(library.Localizations);
220
}
221
}
222
+
223
+ foreach (var localization in localizations)
224
+ {
225
+ context.VariableResolver.AddLocalization(localization);
226
+ }
227
+
228
+ // Gather all the wix variables.
229
+ var wixVariableTuples = context.IntermediateRepresentation.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>();
230
+ foreach (var tuple in wixVariableTuples)
231
+ {
232
+ context.VariableResolver.AddVariable(tuple.SourceLineNumbers, tuple.WixVariable, tuple.Value, tuple.Overridable);
233
+ }
234
}
235
}
236
}
src/WixToolset.Core/WixVariableResolver.cs
+59
-127
@@ -4,72 +4,82 @@ namespace WixToolset.Core
4
{
5
using System;
6
using System.Collections.Generic;
7
- using System.Diagnostics.CodeAnalysis;
8
- using System.Globalization;
7
using System.Text;
10
- using System.Text.RegularExpressions;
8
using WixToolset.Data;
9
+ using WixToolset.Data.Bind;
10
using WixToolset.Extensibility.Services;
11
12
/// <summary>
13
/// WiX variable resolver.
14
/// </summary>
17
- internal sealed class WixVariableResolver : IBindVariableResolver
15
+ internal sealed class WixVariableResolver : IVariableResolver
16
{
19
- private Dictionary<string, string> wixVariables;
17
+ private Dictionary<string, BindVariable> locVariables;
18
+ private Dictionary<string, BindVariable> wixVariables;
19
+ private Dictionary<string, LocalizedControl> localizedControls;
20
21
/// <summary>
22
/// Instantiate a new WixVariableResolver.
23
/// </summary>
24
- public WixVariableResolver(IMessaging messaging, Localizer localizer = null)
24
+ public WixVariableResolver(IMessaging messaging)
25
{
26
- this.wixVariables = new Dictionary<string, string>();
26
+ this.locVariables = new Dictionary<string, BindVariable>();
27
+ this.wixVariables = new Dictionary<string, BindVariable>();
28
+ this.Codepage = -1;
29
this.Messaging = messaging;
28
- this.Localizer = localizer;
30
}
31
32
private IMessaging Messaging { get; }
33
33
- private Localizer Localizer { get; }
34
+ public int Codepage { get; private set; }
35
35
- /// <summary>
36
- /// Gets the count of variables added to the resolver.
37
- /// </summary>
36
public int VariableCount => this.wixVariables.Count;
37
40
- /// <summary>
41
- /// Add a variable.
42
- /// </summary>
43
- /// <param name="name">The name of the variable.</param>
44
- /// <param name="value">The value of the variable.</param>
45
- /// <param name="overridable">Indicates whether the variable can be overridden by an existing variable.</param>
46
- public void AddVariable(string name, string value, bool overridable)
38
+ public void AddLocalization(Localization localization)
39
{
48
- try
40
+ if (-1 == this.Codepage)
41
{
50
- this.wixVariables.Add(name, value);
42
+ this.Codepage = localization.Codepage;
43
}
52
- catch (ArgumentException)
44
+
45
+ foreach (var variable in localization.Variables)
46
{
54
- if (!overridable)
47
+ if (!TryAddWixVariable(this.locVariables, variable))
48
{
56
- throw;
49
+ this.Messaging.Write(ErrorMessages.DuplicateLocalizationIdentifier(variable.SourceLineNumbers, variable.Id));
50
+ }
51
+ }
52
+
53
+ foreach (KeyValuePair<string, LocalizedControl> localizedControl in localization.LocalizedControls)
54
+ {
55
+ if (!this.localizedControls.ContainsKey(localizedControl.Key))
56
+ {
57
+ this.localizedControls.Add(localizedControl.Key, localizedControl.Value);
58
}
59
}
60
}
61
61
- /// <summary>
62
- /// Resolve the wix variables in a value.
63
- /// </summary>
64
- /// <param name="sourceLineNumbers">The source line information for the value.</param>
65
- /// <param name="value">The value to resolve.</param>
66
- /// <param name="localizationOnly">true to only resolve localization variables; false otherwise.</param>
67
- /// <returns>The resolved value.</returns>
68
- public BindVariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly)
62
+ public void AddVariable(SourceLineNumber sourceLineNumber, string name, string value, bool overridable)
63
+ {
64
+ var bindVariable = new BindVariable { Id = name, Value = value, Overridable = overridable, SourceLineNumbers = sourceLineNumber };
65
+
66
+ if (!TryAddWixVariable(this.wixVariables, bindVariable))
67
+ {
68
+ this.Messaging.Write(ErrorMessages.WixVariableCollision(sourceLineNumber, name));
69
+ }
70
+ }
71
+
72
+ public VariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly)
73
{
74
return this.ResolveVariables(sourceLineNumbers, value, localizationOnly, true);
75
}
76
77
+ public bool TryGetLocalizedControl(string dialog, string control, out LocalizedControl localizedControl)
78
+ {
79
+ var key = LocalizedControl.GetKey(dialog, control);
80
+ return this.localizedControls.TryGetValue(key, out localizedControl);
81
+ }
82
+
83
/// <summary>
84
/// Resolve the wix variables in a value.
85
/// </summary>
@@ -80,23 +90,23 @@ namespace WixToolset.Core
90
/// <param name="isDefault">true if the resolved value was the default.</param>
91
/// <param name="delayedResolve">true if the value has variables that cannot yet be resolved.</param>
92
/// <returns>The resolved value.</returns>
83
- internal BindVariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, bool errorOnUnknown)
93
+ internal VariableResolution ResolveVariables(SourceLineNumber sourceLineNumbers, string value, bool localizationOnly, bool errorOnUnknown)
94
{
85
- MatchCollection matches = Common.WixVariableRegex.Matches(value);
95
+ var matches = Common.WixVariableRegex.Matches(value);
96
97
// the value is the default unless its substituted further down
88
- var result = new BindVariableResolution { IsDefault = true, Value = value };
98
+ var result = new VariableResolution { IsDefault = true, Value = value };
99
100
if (0 < matches.Count)
101
{
92
- StringBuilder sb = new StringBuilder(value);
102
+ var sb = new StringBuilder(value);
103
104
// notice how this code walks backward through the list
105
// because it modifies the string as we through it
106
for (int i = matches.Count - 1; 0 <= i; i--)
107
{
98
- string variableNamespace = matches[i].Groups["namespace"].Value;
99
- string variableId = matches[i].Groups["fullname"].Value;
108
+ var variableNamespace = matches[i].Groups["namespace"].Value;
109
+ var variableId = matches[i].Groups["fullname"].Value;
110
string variableDefaultValue = null;
111
112
// get the default value if one was specified
@@ -142,7 +152,10 @@ namespace WixToolset.Core
152
this.Messaging.Write(WarningMessages.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId));
153
}
154
145
- resolvedValue = this.Localizer?.GetLocalizedValue(variableId);
155
+ if (this.locVariables.TryGetValue(variableId, out var bindVariable))
156
+ {
157
+ resolvedValue = bindVariable.Value;
158
+ }
159
}
160
else if (!localizationOnly && "wix" == variableNamespace)
161
{
@@ -153,9 +166,9 @@ namespace WixToolset.Core
166
}
167
else
168
{
156
- if (this.wixVariables.TryGetValue(variableId, out resolvedValue))
169
+ if (this.wixVariables.TryGetValue(variableId, out var bindVariable))
170
{
158
- resolvedValue = resolvedValue ?? String.Empty;
171
+ resolvedValue = bindVariable.Value ?? String.Empty;
172
result.IsDefault = false;
173
}
174
else if (null != variableDefaultValue) // default the resolved value to the inline value if one was specified
@@ -198,96 +211,15 @@ namespace WixToolset.Core
211
return result;
212
}
213
201
- /// <summary>
202
- /// Try to find localization information for dialog and (optional) control.
203
- /// </summary>
204
- /// <param name="dialog">Dialog identifier.</param>
205
- /// <param name="control">Optional control identifier.</param>
206
- /// <param name="localizedControl">Found localization information.</param>
207
- /// <returns>True if localized control was found, otherwise false.</returns>
208
- public bool TryGetLocalizedControl(string dialog, string control, out LocalizedControl localizedControl)
209
- {
210
- localizedControl = this.Localizer?.GetLocalizedControl(dialog, control);
211
- return localizedControl != null;
212
- }
213
-
214
- /// <summary>
215
- /// Resolve the delay variables in a value.
216
- /// </summary>
217
- /// <param name="sourceLineNumbers">The source line information for the value.</param>
218
- /// <param name="value">The value to resolve.</param>
219
- /// <param name="resolutionData"></param>
220
- /// <returns>The resolved value.</returns>
221
- [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "sourceLineNumbers")]
222
- [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "This string is not round tripped, and not used for any security decisions")]
223
- public static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary<string, string> resolutionData)
214
+ private static bool TryAddWixVariable(IDictionary<string, BindVariable> variables, BindVariable variable)
215
{
225
- MatchCollection matches = Common.WixVariableRegex.Matches(value);
226
-
227
- if (0 < matches.Count)
216
+ if (!variables.TryGetValue(variable.Id, out var existingWixVariableRow) || (existingWixVariableRow.Overridable && !variable.Overridable))
217
{
229
- StringBuilder sb = new StringBuilder(value);
230
-
231
- // notice how this code walks backward through the list
232
- // because it modifies the string as we go through it
233
- for (int i = matches.Count - 1; 0 <= i; i--)
234
- {
235
- string variableNamespace = matches[i].Groups["namespace"].Value;
236
- string variableId = matches[i].Groups["fullname"].Value;
237
- string variableDefaultValue = null;
238
- string variableScope = null;
239
-
240
- // get the default value if one was specified
241
- if (matches[i].Groups["value"].Success)
242
- {
243
- variableDefaultValue = matches[i].Groups["value"].Value;
244
- }
245
-
246
- // get the scope if one was specified
247
- if (matches[i].Groups["scope"].Success)
248
- {
249
- variableScope = matches[i].Groups["scope"].Value;
250
- if ("bind" == variableNamespace)
251
- {
252
- variableId = matches[i].Groups["name"].Value;
253
- }
254
- }
255
-
256
- // check for an escape sequence of !! indicating the match is not a variable expression
257
- if (0 < matches[i].Index && '!' == sb[matches[i].Index - 1])
258
- {
259
- sb.Remove(matches[i].Index - 1, 1);
260
- }
261
- else
262
- {
263
- string key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", variableId, variableScope).ToLower(CultureInfo.InvariantCulture);
264
- string resolvedValue = variableDefaultValue;
265
-
266
- if (resolutionData.ContainsKey(key))
267
- {
268
- resolvedValue = resolutionData[key];
269
- }
270
-
271
- if ("bind" == variableNamespace)
272
- {
273
- // insert the resolved value if it was found or display an error
274
- if (null != resolvedValue)
275
- {
276
- sb.Remove(matches[i].Index, matches[i].Length);
277
- sb.Insert(matches[i].Index, resolvedValue);
278
- }
279
- else
280
- {
281
- throw new WixException(ErrorMessages.UnresolvedBindReference(sourceLineNumbers, value));
282
- }
283
- }
284
- }
285
- }
286
-
287
- value = sb.ToString();
218
+ variables[variable.Id] = variable;
219
+ return true;
220
}
221
290
- return value;
222
+ return variable.Overridable;
223
}
224
}
225
}