// 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.Data
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using WixToolset.Data.Msi;
using WixToolset.Data.Rows;
///
/// Object that represents a localization file.
///
public sealed class Localization
{
private static string XmlElementName = "localization";
private Dictionary variables = new Dictionary();
private Dictionary localizedControls = new Dictionary();
///
/// Instantiates a new localization object.
///
public Localization(int codepage, string culture, IDictionary variables, IDictionary localizedControls)
{
this.Codepage = codepage;
this.Culture = String.IsNullOrEmpty(culture) ? String.Empty : culture.ToLowerInvariant();
this.variables = new Dictionary(variables);
this.localizedControls = new Dictionary(localizedControls);
}
///
/// Gets the codepage.
///
/// The codepage.
public int Codepage { get; private set; }
///
/// Gets the culture.
///
/// The culture.
public string Culture { get; private set; }
///
/// Gets the variables.
///
/// The variables.
public ICollection Variables
{
get { return this.variables.Values; }
}
///
/// Gets the localized controls.
///
/// The localized controls.
public ICollection> LocalizedControls
{
get { return this.localizedControls; }
}
///
/// Merge the information from another localization object into this one.
///
/// The localization object to be merged into this one.
public void Merge(Localization localization)
{
foreach (WixVariableRow wixVariableRow in localization.Variables)
{
WixVariableRow existingWixVariableRow;
if (!this.variables.TryGetValue(wixVariableRow.Id, out existingWixVariableRow) || (existingWixVariableRow.Overridable && !wixVariableRow.Overridable))
{
variables[wixVariableRow.Id] = wixVariableRow;
}
else if (!wixVariableRow.Overridable)
{
throw new WixException(WixDataErrors.DuplicateLocalizationIdentifier(wixVariableRow.SourceLineNumbers, wixVariableRow.Id));
}
}
}
///
/// Loads a localization file from a stream.
///
/// XmlReader where the intermediate is persisted.
/// Collection containing TableDefinitions to use when loading the localization file.
/// Returns the loaded localization.
internal static Localization Read(XmlReader reader, TableDefinitionCollection tableDefinitions)
{
Debug.Assert("localization" == reader.LocalName);
int codepage = 0;
string culture = null;
bool empty = reader.IsEmptyElement;
while (reader.MoveToNextAttribute())
{
switch (reader.Name)
{
case "codepage":
codepage = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture);
break;
case "culture":
culture = reader.Value;
break;
}
}
TableDefinition wixVariableTable = tableDefinitions["WixVariable"];
Dictionary variables = new Dictionary();
Dictionary localizedControls = new Dictionary();
if (!empty)
{
bool done = false;
while (!done && reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.LocalName)
{
case "string":
WixVariableRow row = Localization.ReadString(reader, wixVariableTable);
variables.Add(row.Id, row);
break;
case "ui":
LocalizedControl ui = Localization.ReadUI(reader);
localizedControls.Add(ui.GetKey(), ui);
break;
default:
throw new XmlException();
}
break;
case XmlNodeType.EndElement:
done = true;
break;
}
}
if (!done)
{
throw new XmlException();
}
}
return new Localization(codepage, culture, variables, localizedControls);
}
///
/// Writes a localization file into an XML format.
///
/// XmlWriter where the localization file should persist itself as XML.
internal void Write(XmlWriter writer)
{
writer.WriteStartElement(Localization.XmlElementName, Library.XmlNamespaceUri);
if (-1 != this.Codepage)
{
writer.WriteAttributeString("codepage", this.Codepage.ToString(CultureInfo.InvariantCulture));
}
if (!String.IsNullOrEmpty(this.Culture))
{
writer.WriteAttributeString("culture", this.Culture);
}
foreach (WixVariableRow wixVariableRow in this.variables.Values)
{
writer.WriteStartElement("string", Library.XmlNamespaceUri);
writer.WriteAttributeString("id", wixVariableRow.Id);
if (wixVariableRow.Overridable)
{
writer.WriteAttributeString("overridable", "yes");
}
writer.WriteCData(wixVariableRow.Value);
writer.WriteEndElement();
}
foreach (string controlKey in this.localizedControls.Keys)
{
writer.WriteStartElement("ui", Library.XmlNamespaceUri);
string[] controlKeys = controlKey.Split('/');
string dialog = controlKeys[0];
string control = controlKeys[1];
if (!String.IsNullOrEmpty(dialog))
{
writer.WriteAttributeString("dialog", dialog);
}
if (!String.IsNullOrEmpty(control))
{
writer.WriteAttributeString("control", control);
}
LocalizedControl localizedControl = this.localizedControls[controlKey];
if (Common.IntegerNotSet != localizedControl.X)
{
writer.WriteAttributeString("x", localizedControl.X.ToString());
}
if (Common.IntegerNotSet != localizedControl.Y)
{
writer.WriteAttributeString("y", localizedControl.Y.ToString());
}
if (Common.IntegerNotSet != localizedControl.Width)
{
writer.WriteAttributeString("width", localizedControl.Width.ToString());
}
if (Common.IntegerNotSet != localizedControl.Height)
{
writer.WriteAttributeString("height", localizedControl.Height.ToString());
}
if (MsiInterop.MsidbControlAttributesRTLRO == (localizedControl.Attributes & MsiInterop.MsidbControlAttributesRTLRO))
{
writer.WriteAttributeString("rightToLeft", "yes");
}
if (MsiInterop.MsidbControlAttributesRightAligned == (localizedControl.Attributes & MsiInterop.MsidbControlAttributesRightAligned))
{
writer.WriteAttributeString("rightAligned", "yes");
}
if (MsiInterop.MsidbControlAttributesLeftScroll == (localizedControl.Attributes & MsiInterop.MsidbControlAttributesLeftScroll))
{
writer.WriteAttributeString("leftScroll", "yes");
}
if (!String.IsNullOrEmpty(localizedControl.Text))
{
writer.WriteCData(localizedControl.Text);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
///
/// Loads a localization file from a stream.
///
/// XmlReader where the intermediate is persisted.
/// Collection containing TableDefinitions to use when loading the localization file.
/// Returns the loaded localization.
private static WixVariableRow ReadString(XmlReader reader, TableDefinition wixVariableTable)
{
Debug.Assert("string" == reader.LocalName);
string id = null;
string value = null;
bool overridable = false;
bool empty = reader.IsEmptyElement;
while (reader.MoveToNextAttribute())
{
switch (reader.Name)
{
case "id":
id = reader.Value;
break;
case "overridable":
overridable = reader.Value.Equals("yes");
break;
}
}
if (!empty)
{
reader.Read();
value = reader.Value;
reader.Read();
if (XmlNodeType.EndElement != reader.NodeType)
{
throw new XmlException();
}
}
WixVariableRow wixVariableRow = new WixVariableRow(SourceLineNumber.CreateFromUri(reader.BaseURI), wixVariableTable);
wixVariableRow.Id = id;
wixVariableRow.Overridable = overridable;
wixVariableRow.Value = value;
return wixVariableRow;
}
private static LocalizedControl ReadUI(XmlReader reader)
{
Debug.Assert("ui" == reader.LocalName);
string dialog = null;
string control = null;
int x = Common.IntegerNotSet;
int y = Common.IntegerNotSet;
int width = Common.IntegerNotSet;
int height = Common.IntegerNotSet;
int attributes = Common.IntegerNotSet;
string text = null;
bool empty = reader.IsEmptyElement;
while (reader.MoveToNextAttribute())
{
switch (reader.Name)
{
case "dialog":
dialog = reader.Value;
break;
case "control":
control = reader.Value;
break;
case "x":
x = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture);
break;
case "y":
y = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture);
break;
case "width":
width = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture);
break;
case "height":
height = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture);
break;
case "attributes":
attributes = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture);
break;
}
}
if (!empty)
{
reader.Read();
text = reader.Value;
reader.Read();
if (XmlNodeType.EndElement != reader.NodeType)
{
throw new XmlException();
}
}
return new LocalizedControl(dialog, control, x, y, width, height, attributes, text);
}
}
}