// 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. namespace WixToolset.Core { using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using WixToolset.Core.Bind; using WixToolset.Data; using WixToolset.Data.Symbols; using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; /// /// Resolver for the WiX toolset. /// internal class Resolver : IResolver { internal Resolver(IServiceProvider serviceProvider) { this.ServiceProvider = serviceProvider; this.Messaging = serviceProvider.GetService(); } private IServiceProvider ServiceProvider { get; } private IMessaging Messaging { get; } public IResolveResult Resolve(IResolveContext context) { foreach (var extension in context.Extensions) { extension.PreResolve(context); } ResolveResult resolveResult = null; try { var filteredLocalizations = FilterLocalizations(context); var variableResolver = this.CreateVariableResolver(context, filteredLocalizations); this.LocalizeUI(variableResolver, context.IntermediateRepresentation); resolveResult = this.DoResolve(context, variableResolver); var primaryLocalization = filteredLocalizations.FirstOrDefault(); if (primaryLocalization != null) { this.TryGetCultureInfo(primaryLocalization.Culture, out var cultureInfo); resolveResult.Codepage = primaryLocalization.Codepage ?? cultureInfo?.TextInfo.ANSICodePage; resolveResult.SummaryInformationCodepage = primaryLocalization.SummaryInformationCodepage ?? primaryLocalization.Codepage ?? cultureInfo?.TextInfo.ANSICodePage; resolveResult.PackageLcid = cultureInfo?.LCID; } } finally { foreach (var extension in context.Extensions) { extension.PostResolve(resolveResult); } } return resolveResult; } private ResolveResult DoResolve(IResolveContext context, IVariableResolver variableResolver) { var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch); var filesWithEmbeddedFiles = new ExtractEmbeddedFiles(); IEnumerable delayedFields; { var command = new ResolveFieldsCommand(); command.Messaging = this.Messaging; command.BuildingPatch = buildingPatch; command.VariableResolver = variableResolver; command.BindPaths = context.BindPaths; command.Extensions = context.Extensions; command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; command.IntermediateFolder = context.IntermediateFolder; command.Intermediate = context.IntermediateRepresentation; command.SupportDelayedResolution = true; command.AllowUnresolvedVariables = context.AllowUnresolvedVariables; command.Execute(); delayedFields = command.DelayedFields; } #if TODO_PATCHING if (context.IntermediateRepresentation.SubStorages != null) { foreach (SubStorage transform in context.IntermediateRepresentation.SubStorages) { var command = new ResolveFieldsCommand(); command.BuildingPatch = buildingPatch; command.BindVariableResolver = context.WixVariableResolver; command.BindPaths = context.BindPaths; command.Extensions = context.Extensions; command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; command.IntermediateFolder = context.IntermediateFolder; command.Intermediate = context.IntermediateRepresentation; command.SupportDelayedResolution = false; command.Execute(); } } #endif var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles(); context.IntermediateRepresentation.UpdateLevel(IntermediateLevels.Resolved); return new ResolveResult { ExpectedEmbeddedFiles = expectedEmbeddedFiles, DelayedFields = delayedFields, IntermediateRepresentation = context.IntermediateRepresentation }; } /// /// Localize dialogs and controls. /// private void LocalizeUI(IVariableResolver variableResolver, Intermediate intermediate) { foreach (var section in intermediate.Sections) { foreach (var symbol in section.Symbols.OfType()) { if (variableResolver.TryGetLocalizedControl(symbol.Id.Id, null, out var localizedControl)) { if (CompilerConstants.IntegerNotSet != localizedControl.X) { symbol.HCentering = localizedControl.X; } if (CompilerConstants.IntegerNotSet != localizedControl.Y) { symbol.VCentering = localizedControl.Y; } if (CompilerConstants.IntegerNotSet != localizedControl.Width) { symbol.Width = localizedControl.Width; } if (CompilerConstants.IntegerNotSet != localizedControl.Height) { symbol.Height = localizedControl.Height; } symbol.RightAligned |= localizedControl.RightAligned; symbol.RightToLeft |= localizedControl.RightToLeft; symbol.LeftScroll |= localizedControl.LeftScroll; if (!String.IsNullOrEmpty(localizedControl.Text)) { symbol.Title = localizedControl.Text; } } } foreach (var symbol in section.Symbols.OfType()) { if (variableResolver.TryGetLocalizedControl(symbol.DialogRef, symbol.Control, out var localizedControl)) { if (CompilerConstants.IntegerNotSet != localizedControl.X) { symbol.X = localizedControl.X; } if (CompilerConstants.IntegerNotSet != localizedControl.Y) { symbol.Y = localizedControl.Y; } if (CompilerConstants.IntegerNotSet != localizedControl.Width) { symbol.Width = localizedControl.Width; } if (CompilerConstants.IntegerNotSet != localizedControl.Height) { symbol.Height = localizedControl.Height; } symbol.RightAligned |= localizedControl.RightAligned; symbol.RightToLeft |= localizedControl.RightToLeft; symbol.LeftScroll |= localizedControl.LeftScroll; if (!String.IsNullOrEmpty(localizedControl.Text)) { symbol.Text = localizedControl.Text; } } } } } private IVariableResolver CreateVariableResolver(IResolveContext context, IEnumerable filteredLocalizations) { var variableResolver = this.ServiceProvider.GetService(); foreach (var localization in filteredLocalizations) { variableResolver.AddLocalization(localization); } // Gather all the wix variables. var wixVariableSymbols = context.IntermediateRepresentation.Sections.SelectMany(s => s.Symbols).OfType(); foreach (var symbol in wixVariableSymbols) { variableResolver.AddVariable(symbol.SourceLineNumbers, symbol.Id.Id, symbol.Value, symbol.Overridable); } return variableResolver; } private bool TryGetCultureInfo(string culture, out CultureInfo cultureInfo) { cultureInfo = null; if (!String.IsNullOrEmpty(culture)) { try { cultureInfo = new CultureInfo(culture, useUserOverride: false); } catch { this.Messaging.Write(""); } } return cultureInfo != null; } private static IEnumerable FilterLocalizations(IResolveContext context) { var result = new List(); var filter = CalculateCultureFilter(context); var localizations = context.Localizations.Concat(context.IntermediateRepresentation.Localizations).ToList(); AddFilteredLocalizations(result, filter, localizations); // Filter localizations provided by extensions with data. var creator = context.ServiceProvider.GetService(); foreach (var data in context.ExtensionData) { var library = data.GetLibrary(creator); if (library?.Localizations != null && library.Localizations.Any()) { var extensionFilter = (!filter.Any() && data.DefaultCulture != null) ? new[] { data.DefaultCulture } : filter; AddFilteredLocalizations(result, extensionFilter, library.Localizations); } } return result; } private static IEnumerable CalculateCultureFilter(IResolveContext context) { var filter = context.FilterCultures ?? Array.Empty(); // If no filter was specified, look for a language neutral localization file specified // from the command-line (not embedded in the intermediate). If found, filter on language // neutral. if (!filter.Any() && context.Localizations.Any(l => String.IsNullOrEmpty(l.Culture))) { filter = new[] { String.Empty }; } return filter; } private static void AddFilteredLocalizations(List result, IEnumerable filter, IEnumerable localizations) { // If there is no filter, return all localizations. if (!filter.Any()) { result.AddRange(localizations); } else // filter localizations in order specified by the filter { foreach (var culture in filter) { result.AddRange(localizations.Where(l => culture.Equals(l.Culture, StringComparison.OrdinalIgnoreCase) || String.IsNullOrEmpty(l.Culture))); } } } } }