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