main
cs 185 lines 7.76 KB
Raw
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.Bind
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Symbols;
10 using WixToolset.Extensibility;
11 using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services;
13
14 /// <summary>
15 /// Resolve source fields in the tables included in the output
16 /// </summary>
17 internal class ResolveFieldsCommand
18 {
19 public ResolveFieldsCommand(IMessaging messaging, IFileResolver fileResolver, IVariableResolver variableResolver, IReadOnlyCollection<IBindPath> bindPaths, IReadOnlyCollection<IResolverExtension> extensions, ExtractEmbeddedFiles filesWithEmbeddedFiles, string intermediateFolder, Intermediate intermediate, bool allowUnresolvedVariables)
20 {
21 this.Messaging = messaging;
22 this.FileResolver = fileResolver;
23 this.VariableResolver = variableResolver;
24 this.BindPaths = bindPaths;
25 this.Extensions = extensions;
26 this.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
27 this.IntermediateFolder = intermediateFolder;
28 this.Intermediate = intermediate;
29 this.AllowUnresolvedVariables = allowUnresolvedVariables;
30 }
31
32 private IMessaging Messaging { get; }
33
34 private IFileResolver FileResolver { get; }
35
36 private IVariableResolver VariableResolver { get; }
37
38 private IEnumerable<IBindPath> BindPaths { get; }
39
40 private IEnumerable<IResolverExtension> Extensions { get; }
41
42 private ExtractEmbeddedFiles FilesWithEmbeddedFiles { get; }
43
44 private string IntermediateFolder { get; }
45
46 private Intermediate Intermediate { get; }
47
48 private bool AllowUnresolvedVariables { get; }
49
50 public IReadOnlyCollection<DelayedField> DelayedFields { get; private set; }
51
52 public void Execute()
53 {
54 var delayedFields = new List<DelayedField>();
55
56 var bindPaths = this.BindPaths.Where(b => b.Stage == BindStage.Normal).ToList();
57
58 // Build the column lookup only when needed.
59 Dictionary<string, WixCustomTableColumnSymbol> customColumnsById = null;
60
61 foreach (var symbol in this.Intermediate.Sections.SelectMany(s => s.Symbols))
62 {
63 foreach (var field in symbol.Fields.Where(f => !f.IsNull()))
64 {
65 var fieldType = field.Type;
66
67 // Custom table cells require an extra look up to the column definition as the
68 // cell's data type is always a string (because strings can store anything) but
69 // the column definition may be more specific.
70 if (symbol.Definition.Type == SymbolDefinitionType.WixCustomTableCell)
71 {
72 // We only care about the Data in a CustomTable cell.
73 if (field.Name != nameof(WixCustomTableCellSymbolFields.Data))
74 {
75 continue;
76 }
77
78 if (customColumnsById == null)
79 {
80 customColumnsById = this.Intermediate.Sections.SelectMany(s => s.Symbols.OfType<WixCustomTableColumnSymbol>()).ToDictionary(t => t.Id.Id);
81 }
82
83 if (customColumnsById.TryGetValue(symbol.Fields[(int)WixCustomTableCellSymbolFields.TableRef].AsString() + "/" + symbol.Fields[(int)WixCustomTableCellSymbolFields.ColumnRef].AsString(), out var customColumn))
84 {
85 fieldType = customColumn.Type;
86 }
87 }
88
89 var beforeErrorCount = this.Messaging.ErrorCount;
90
91 // resolve localization and wix variables
92 if (fieldType == IntermediateFieldType.String)
93 {
94 var original = field.AsString();
95 if (!String.IsNullOrEmpty(original))
96 {
97 var resolution = this.VariableResolver.ResolveVariables(symbol.SourceLineNumbers, original, !this.AllowUnresolvedVariables);
98 if (resolution.UpdatedValue)
99 {
100 field.Set(resolution.Value);
101 }
102
103 if (resolution.DelayedResolve)
104 {
105 delayedFields.Add(new DelayedField(symbol, field));
106 }
107 }
108 }
109
110 // Move to next symbol if we've hit an error resolving variables.
111 if (beforeErrorCount < this.Messaging.ErrorCount)
112 {
113 continue;
114 }
115
116 // Resolve file paths
117 if (fieldType == IntermediateFieldType.Path)
118 {
119 this.ResolvePathField(this.FileResolver, bindPaths, symbol, field);
120 }
121 }
122 }
123
124 this.DelayedFields = delayedFields;
125 }
126
127 private void ResolvePathField(IFileResolver fileResolver, IEnumerable<IBindPath> bindPaths, IntermediateSymbol symbol, IntermediateField field)
128 {
129 var fieldValue = field.AsPath();
130 var originalFieldPath = fieldValue.Path;
131
132 // If the file is embedded and if the previous value has a bind variable in the path
133 // which gets modified by resolving the previous value again then switch to that newly
134 // resolved path instead of using the embedded file.
135 if (fieldValue.Embed)
136 {
137 if (field.PreviousValue != null)
138 {
139 var resolution = this.VariableResolver.ResolveVariables(symbol.SourceLineNumbers, field.PreviousValue.AsString(), errorOnUnknown: false);
140
141 if (resolution.UpdatedValue && !resolution.IsDefault)
142 {
143 fieldValue = new IntermediateFieldPathValue { Path = resolution.Value };
144 }
145 }
146 }
147 else // resolve path field for bind variables.
148 {
149 var resolution = this.VariableResolver.ResolveVariables(symbol.SourceLineNumbers, fieldValue.Path, errorOnUnknown: false);
150
151 if (resolution.UpdatedValue)
152 {
153 field.Set(resolution.Value);
154
155 fieldValue = field.AsPath();
156 }
157 }
158
159 // If we're still using the embedded file.
160 if (fieldValue.Embed)
161 {
162 // Set the path to the embedded file once where it will be extracted.
163 var extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileToExtract(fieldValue.BaseUri, fieldValue.Path, this.IntermediateFolder);
164
165 field.Set(extractPath);
166 }
167 else if (fieldValue.Path != null)
168 {
169 try
170 {
171 var resolvedPath = fileResolver.ResolveFile(fieldValue.Path, this.Extensions, bindPaths, BindStage.Normal, symbol.SourceLineNumbers, symbol.Definition);
172
173 if (!String.Equals(originalFieldPath, resolvedPath, StringComparison.OrdinalIgnoreCase))
174 {
175 field.Set(resolvedPath);
176 }
177 }
178 catch (WixException e)
179 {
180 this.Messaging.Write(e.Error);
181 }
182 }
183 }
184 }
185 }