From a4f5a5a042c00254607fbecdf132a2e2a91a1bdd Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Fri, 1 Mar 2019 07:33:20 -0800 Subject: Rename ILocalizer to ILocalizationParser --- src/WixToolset.Core/LocalizationParser.cs | 324 ++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 src/WixToolset.Core/LocalizationParser.cs (limited to 'src/WixToolset.Core/LocalizationParser.cs') diff --git a/src/WixToolset.Core/LocalizationParser.cs b/src/WixToolset.Core/LocalizationParser.cs new file mode 100644 index 00000000..f7f86a54 --- /dev/null +++ b/src/WixToolset.Core/LocalizationParser.cs @@ -0,0 +1,324 @@ +// 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.Xml.Linq; + using WixToolset.Core.Native; + using WixToolset.Data; + using WixToolset.Data.Bind; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Services; + + internal class LocalizationParser : ILocalizationParser + { + public static readonly XNamespace WxlNamespace = "http://wixtoolset.org/schemas/v4/wxl"; + private static string XmlElementName = "WixLocalization"; + + internal LocalizationParser(IServiceProvider serviceProvider) + { + this.Messaging = serviceProvider.GetService(); + } + + private IMessaging Messaging { get; } + + public Localization ParseLocalization(string path) + { + var document = XDocument.Load(path); + return this.ParseLocalization(document); + } + + public Localization ParseLocalization(XDocument document) + { + XElement root = document.Root; + Localization localization = null; + + SourceLineNumber sourceLineNumbers = SourceLineNumber.CreateFromXObject(root); + if (LocalizationParser.XmlElementName == root.Name.LocalName) + { + if (LocalizationParser.WxlNamespace == root.Name.Namespace) + { + localization = ParseWixLocalizationElement(this.Messaging, root); + } + else // invalid or missing namespace + { + if (null == root.Name.Namespace) + { + this.Messaging.Write(ErrorMessages.InvalidWixXmlNamespace(sourceLineNumbers, LocalizationParser.XmlElementName, LocalizationParser.WxlNamespace.NamespaceName)); + } + else + { + this.Messaging.Write(ErrorMessages.InvalidWixXmlNamespace(sourceLineNumbers, LocalizationParser.XmlElementName, root.Name.LocalName, LocalizationParser.WxlNamespace.NamespaceName)); + } + } + } + else + { + this.Messaging.Write(ErrorMessages.InvalidDocumentElement(sourceLineNumbers, root.Name.LocalName, "localization", LocalizationParser.XmlElementName)); + } + + return localization; + } + + /// + /// Adds a WixVariableRow to a dictionary while performing the expected override checks. + /// + /// Dictionary of variable rows. + /// Row to add to the variables dictionary. + private static void AddWixVariable(IMessaging messaging, IDictionary variables, BindVariable wixVariableRow) + { + if (!variables.TryGetValue(wixVariableRow.Id, out var existingWixVariableRow) || (existingWixVariableRow.Overridable && !wixVariableRow.Overridable)) + { + variables[wixVariableRow.Id] = wixVariableRow; + } + else if (!wixVariableRow.Overridable) + { + messaging.Write(ErrorMessages.DuplicateLocalizationIdentifier(wixVariableRow.SourceLineNumbers, wixVariableRow.Id)); + } + } + + /// + /// Parses the WixLocalization element. + /// + /// Element to parse. + private static Localization ParseWixLocalizationElement(IMessaging messaging, XElement node) + { + int codepage = -1; + string culture = null; + SourceLineNumber sourceLineNumbers = SourceLineNumber.CreateFromXObject(node); + + foreach (XAttribute attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || LocalizationParser.WxlNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Codepage": + codepage = Common.GetValidCodePage(attrib.Value, true, false, sourceLineNumbers); + break; + case "Culture": + culture = attrib.Value; + break; + case "Language": + // do nothing; @Language is used for locutil which can't convert Culture to lcid + break; + default: + Common.UnexpectedAttribute(messaging, sourceLineNumbers, attrib); + break; + } + } + else + { + Common.UnexpectedAttribute(messaging, sourceLineNumbers, attrib); + } + } + + Dictionary variables = new Dictionary(); + Dictionary localizedControls = new Dictionary(); + + foreach (XElement child in node.Elements()) + { + if (LocalizationParser.WxlNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "String": + LocalizationParser.ParseString(messaging, child, variables); + break; + + case "UI": + LocalizationParser.ParseUI(messaging, child, localizedControls); + break; + + default: + messaging.Write(ErrorMessages.UnexpectedElement(sourceLineNumbers, node.Name.ToString(), child.Name.ToString())); + break; + } + } + else + { + messaging.Write(ErrorMessages.UnsupportedExtensionElement(sourceLineNumbers, node.Name.ToString(), child.Name.ToString())); + } + } + + return messaging.EncounteredError ? null : new Localization(codepage, culture, variables, localizedControls); + } + + /// + /// Parse a localization string into a WixVariableRow. + /// + /// Element to parse. + private static void ParseString(IMessaging messaging, XElement node, IDictionary variables) + { + string id = null; + bool overridable = false; + SourceLineNumber sourceLineNumbers = SourceLineNumber.CreateFromXObject(node); + + foreach (XAttribute attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || LocalizationParser.WxlNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = Common.GetAttributeIdentifierValue(messaging, sourceLineNumbers, attrib); + break; + case "Overridable": + overridable = YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib); + break; + case "Localizable": + ; // do nothing + break; + default: + messaging.Write(ErrorMessages.UnexpectedAttribute(sourceLineNumbers, attrib.Parent.Name.ToString(), attrib.Name.ToString())); + break; + } + } + else + { + messaging.Write(ErrorMessages.UnsupportedExtensionAttribute(sourceLineNumbers, attrib.Parent.Name.ToString(), attrib.Name.ToString())); + } + } + + string value = Common.GetInnerText(node); + + if (null == id) + { + messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, "String", "Id")); + } + else if (0 == id.Length) + { + messaging.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, "String", "Id", 0)); + } + + if (!messaging.EncounteredError) + { + var variable = new BindVariable + { + SourceLineNumbers = sourceLineNumbers, + Id = id, + Overridable = overridable, + Value = value, + }; + + LocalizationParser.AddWixVariable(messaging, variables, variable); + } + } + + /// + /// Parse a localized control. + /// + /// Element to parse. + /// Dictionary of localized controls. + private static void ParseUI(IMessaging messaging, XElement node, IDictionary localizedControls) + { + string dialog = null; + string control = null; + int x = CompilerConstants.IntegerNotSet; + int y = CompilerConstants.IntegerNotSet; + int width = CompilerConstants.IntegerNotSet; + int height = CompilerConstants.IntegerNotSet; + int attribs = 0; + string text = null; + SourceLineNumber sourceLineNumbers = SourceLineNumber.CreateFromXObject(node); + + foreach (XAttribute attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || LocalizationParser.WxlNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Dialog": + dialog = Common.GetAttributeIdentifierValue(messaging, sourceLineNumbers, attrib); + break; + case "Control": + control = Common.GetAttributeIdentifierValue(messaging, sourceLineNumbers, attrib); + break; + case "X": + x = Common.GetAttributeIntegerValue(messaging, sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Y": + y = Common.GetAttributeIntegerValue(messaging, sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Width": + width = Common.GetAttributeIntegerValue(messaging, sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "Height": + height = Common.GetAttributeIntegerValue(messaging, sourceLineNumbers, attrib, 0, short.MaxValue); + break; + case "RightToLeft": + if (YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib)) + { + attribs |= MsiInterop.MsidbControlAttributesRTLRO; + } + break; + case "RightAligned": + if (YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib)) + { + attribs |= MsiInterop.MsidbControlAttributesRightAligned; + } + break; + case "LeftScroll": + if (YesNoType.Yes == Common.GetAttributeYesNoValue(messaging, sourceLineNumbers, attrib)) + { + attribs |= MsiInterop.MsidbControlAttributesLeftScroll; + } + break; + default: + Common.UnexpectedAttribute(messaging, sourceLineNumbers, attrib); + break; + } + } + else + { + Common.UnexpectedAttribute(messaging, sourceLineNumbers, attrib); + } + } + + text = Common.GetInnerText(node); + + if (String.IsNullOrEmpty(control) && 0 < attribs) + { + if (MsiInterop.MsidbControlAttributesRTLRO == (attribs & MsiInterop.MsidbControlAttributesRTLRO)) + { + messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.ToString(), "RightToLeft", "Control")); + } + else if (MsiInterop.MsidbControlAttributesRightAligned == (attribs & MsiInterop.MsidbControlAttributesRightAligned)) + { + messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.ToString(), "RightAligned", "Control")); + } + else if (MsiInterop.MsidbControlAttributesLeftScroll == (attribs & MsiInterop.MsidbControlAttributesLeftScroll)) + { + messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.ToString(), "LeftScroll", "Control")); + } + } + + if (String.IsNullOrEmpty(control) && String.IsNullOrEmpty(dialog)) + { + messaging.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.ToString(), "Dialog", "Control")); + } + + if (!messaging.EncounteredError) + { + LocalizedControl localizedControl = new LocalizedControl(dialog, control, x, y, width, height, attribs, text); + string key = localizedControl.GetKey(); + if (localizedControls.ContainsKey(key)) + { + if (String.IsNullOrEmpty(localizedControl.Control)) + { + messaging.Write(ErrorMessages.DuplicatedUiLocalization(sourceLineNumbers, localizedControl.Dialog)); + } + else + { + messaging.Write(ErrorMessages.DuplicatedUiLocalization(sourceLineNumbers, localizedControl.Dialog, localizedControl.Control)); + } + } + else + { + localizedControls.Add(key, localizedControl); + } + } + } + } +} -- cgit v1.2.3-55-g6feb