|
|
|
@ -16,7 +16,7 @@ using System.Xml;
@@ -16,7 +16,7 @@ using System.Xml;
|
|
|
|
|
using ICSharpCode.Core; |
|
|
|
|
using ICSharpCode.NRefactory; |
|
|
|
|
using ICSharpCode.SharpDevelop.Dom; |
|
|
|
|
using ICSharpCode.TextEditor.Document; |
|
|
|
|
using ICSharpCode.SharpDevelop.Editor; |
|
|
|
|
using ICSharpCode.XmlEditor; |
|
|
|
|
|
|
|
|
|
namespace ICSharpCode.WixBinding |
|
|
|
@ -25,64 +25,19 @@ namespace ICSharpCode.WixBinding
@@ -25,64 +25,19 @@ namespace ICSharpCode.WixBinding
|
|
|
|
|
/// A Wix document (.wxs or .wxi).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class WixDocument : XmlDocument, IWixPropertyValueProvider |
|
|
|
|
{ |
|
|
|
|
public const string WixSourceFileExtension = ".wxs"; |
|
|
|
|
public const string WixIncludeFileExtension = ".wxi"; |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Class used to store the line number and dialog id of the
|
|
|
|
|
/// dialog element start tag.
|
|
|
|
|
/// </summary>
|
|
|
|
|
class DialogStartElement |
|
|
|
|
{ |
|
|
|
|
int line = -1; |
|
|
|
|
string id = String.Empty; |
|
|
|
|
|
|
|
|
|
public DialogStartElement(int line, string id) |
|
|
|
|
{ |
|
|
|
|
this.id = id; |
|
|
|
|
this.line = line; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public int Line { |
|
|
|
|
get { |
|
|
|
|
return line; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public string Id { |
|
|
|
|
get { |
|
|
|
|
return id; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Default IFileLoader implementation which tries to load
|
|
|
|
|
/// the bitmap from the specified filename. If the file does
|
|
|
|
|
/// not exist the bitmap is not loaded.
|
|
|
|
|
/// </summary>
|
|
|
|
|
class FileLoader : IFileLoader |
|
|
|
|
{ |
|
|
|
|
public Bitmap GetBitmap(string fileName) |
|
|
|
|
{ |
|
|
|
|
if (File.Exists(fileName)) { |
|
|
|
|
return new Bitmap(fileName); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
WixNamespaceManager namespaceManager; |
|
|
|
|
IFileLoader fileLoader; |
|
|
|
|
WixProject project; |
|
|
|
|
string fileName = String.Empty; |
|
|
|
|
|
|
|
|
|
public WixDocument() : this((WixProject)null) |
|
|
|
|
public WixDocument() |
|
|
|
|
: this((WixProject)null) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public WixDocument(WixProject project) : this(project, new FileLoader()) |
|
|
|
|
public WixDocument(WixProject project) |
|
|
|
|
: this(project, new DefaultFileLoader()) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -94,411 +49,82 @@ namespace ICSharpCode.WixBinding
@@ -94,411 +49,82 @@ namespace ICSharpCode.WixBinding
|
|
|
|
|
{ |
|
|
|
|
this.project = project; |
|
|
|
|
this.fileLoader = fileLoader; |
|
|
|
|
|
|
|
|
|
if (fileLoader == null) { |
|
|
|
|
throw new ArgumentException("Cannot be null.", "fileLoader"); |
|
|
|
|
throw new ArgumentNullException("fileLoader"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
namespaceManager = new WixNamespaceManager(NameTable); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the project that this document belongs to.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public WixProject Project { |
|
|
|
|
get { |
|
|
|
|
return project; |
|
|
|
|
} |
|
|
|
|
get { return project; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the filename for the Wix Document.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string FileName { |
|
|
|
|
get { |
|
|
|
|
return fileName; |
|
|
|
|
} |
|
|
|
|
set { |
|
|
|
|
fileName = value; |
|
|
|
|
} |
|
|
|
|
get { return fileName; } |
|
|
|
|
set { fileName = value; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the dialogs from a Wix file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static ReadOnlyCollection<string> GetDialogIds(string fileName) |
|
|
|
|
{ |
|
|
|
|
using (XmlTextReader reader = new XmlTextReader(new FileStream(fileName, FileMode.Open, FileAccess.Read))) { |
|
|
|
|
return GetDialogIds(reader); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the dialogs from a Wix file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static ReadOnlyCollection<string> GetDialogIds(TextReader reader) |
|
|
|
|
{ |
|
|
|
|
XmlTextReader xmlReader = new XmlTextReader(reader); |
|
|
|
|
return GetDialogIds(xmlReader); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the dialog Ids in a Wix file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static ReadOnlyCollection<string> GetDialogIds(XmlTextReader reader) |
|
|
|
|
{ |
|
|
|
|
using (reader) { |
|
|
|
|
List<string> dialogIds = new List<string>(); |
|
|
|
|
object dialogElementName = reader.NameTable.Add("Dialog"); |
|
|
|
|
while (reader.Read()) { |
|
|
|
|
switch (reader.NodeType) { |
|
|
|
|
case XmlNodeType.Element: |
|
|
|
|
if (IsElementMatch(dialogElementName, reader.LocalName)) { |
|
|
|
|
AddDialogId(reader, dialogIds); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return new ReadOnlyCollection<string>(dialogIds); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the line and column where the specified element starts. The column
|
|
|
|
|
/// returned is the column containing the opening tag (<).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The element name.</param>
|
|
|
|
|
/// <param name="id">The id attribute value.</param>
|
|
|
|
|
public static Location GetStartElementLocation(TextReader reader, string name, string id) |
|
|
|
|
{ |
|
|
|
|
XmlTextReader xmlReader = new XmlTextReader(reader); |
|
|
|
|
return GetStartElementLocation(xmlReader, name, id); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the line and column where the specified element starts. The column
|
|
|
|
|
/// returned is the column containing the opening tag (<).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The element name.</param>
|
|
|
|
|
/// <param name="id">The id attribute value.</param>
|
|
|
|
|
public static Location GetStartElementLocation(XmlTextReader reader, string name, string id) |
|
|
|
|
{ |
|
|
|
|
using (reader) { |
|
|
|
|
object elementName = reader.NameTable.Add(name); |
|
|
|
|
while (reader.Read()) { |
|
|
|
|
switch (reader.NodeType) { |
|
|
|
|
case XmlNodeType.Element: |
|
|
|
|
if (IsElementMatch(elementName, reader.LocalName)) { |
|
|
|
|
Location location = GetStartElementLocationIfMatch(reader, id); |
|
|
|
|
if (!location.IsEmpty) { |
|
|
|
|
return location; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return Location.Empty; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the line and column where the specified element ends. The column
|
|
|
|
|
/// returned is the column containing the opening tag (<) of the end element.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The element name.</param>
|
|
|
|
|
/// <param name="id">The id attribute value.</param>
|
|
|
|
|
public static Location GetEndElementLocation(TextReader reader, string name, string id) |
|
|
|
|
{ |
|
|
|
|
XmlTextReader xmlReader = new XmlTextReader(reader); |
|
|
|
|
return GetEndElementLocation(xmlReader, name, id); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the line and column where the specified element ends. The column
|
|
|
|
|
/// returned is the column containing the opening tag (<) of the end element.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The element name.</param>
|
|
|
|
|
/// <param name="id">The id attribute value.</param>
|
|
|
|
|
public static Location GetEndElementLocation(XmlTextReader reader, string name, string id) |
|
|
|
|
{ |
|
|
|
|
using (reader) { |
|
|
|
|
bool startElementFound = false; |
|
|
|
|
object elementName = reader.NameTable.Add(name); |
|
|
|
|
while (reader.Read()) { |
|
|
|
|
switch (reader.NodeType) { |
|
|
|
|
case XmlNodeType.Element: |
|
|
|
|
if (IsElementMatch(elementName, reader.LocalName)) { |
|
|
|
|
Location location = GetStartElementLocationIfMatch(reader, id); |
|
|
|
|
startElementFound = !location.IsEmpty; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case XmlNodeType.EndElement: |
|
|
|
|
if (startElementFound) { |
|
|
|
|
if (IsElementMatch(elementName, reader.LocalName)) { |
|
|
|
|
// Take off an extra 2 from the line position so we get the
|
|
|
|
|
// correct column for the < tag rather than the element name.
|
|
|
|
|
return new Location(reader.LinePosition - 3, reader.LineNumber - 1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return Location.Empty; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the dialog id at the specified line.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="index">Line numbers start from zero.</param>
|
|
|
|
|
/// <returns>The dialog id found at the specified line;
|
|
|
|
|
/// <see langword="null"/> if no dialog found.</returns>
|
|
|
|
|
public static string GetDialogId(TextReader reader, int line) |
|
|
|
|
{ |
|
|
|
|
XmlTextReader xmlReader = new XmlTextReader(reader); |
|
|
|
|
return GetDialogId(xmlReader, line); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the dialog id at the specified line.
|
|
|
|
|
/// Gets a WixDialog object for the specified dialog id.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="index">Line numbers start from zero.</param>
|
|
|
|
|
/// <returns>The dialog id found at the specified line;
|
|
|
|
|
/// <see langword="null"/> if no dialog found.</returns>
|
|
|
|
|
public static string GetDialogId(XmlTextReader reader, int line) |
|
|
|
|
public WixDialog CreateWixDialog(string id, ITextFileReader reader) |
|
|
|
|
{ |
|
|
|
|
// Add one to line since XmlTextReader line numbers start from one.
|
|
|
|
|
++line; |
|
|
|
|
|
|
|
|
|
DialogStartElement dialogStartElement = null; |
|
|
|
|
|
|
|
|
|
using (reader) { |
|
|
|
|
object dialogElementName = reader.NameTable.Add("Dialog"); |
|
|
|
|
while (reader.Read()) { |
|
|
|
|
switch (reader.NodeType) { |
|
|
|
|
case XmlNodeType.Element: |
|
|
|
|
if (IsElementMatch(dialogElementName, reader.LocalName)) { |
|
|
|
|
if (line < reader.LineNumber) { |
|
|
|
|
return null; |
|
|
|
|
} else if (line == reader.LineNumber) { |
|
|
|
|
return GetIdFromCurrentNode(reader); |
|
|
|
|
} else if (reader.IsStartElement()) { |
|
|
|
|
dialogStartElement = new DialogStartElement(reader.LineNumber, GetIdFromCurrentNode(reader)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case XmlNodeType.EndElement: |
|
|
|
|
if (IsElementMatch(dialogElementName, reader.LocalName)) { |
|
|
|
|
if (line > dialogStartElement.Line && line <= reader.LineNumber) { |
|
|
|
|
return dialogStartElement.Id; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
XmlElement dialogElement = GetDialogElement(id); |
|
|
|
|
if (dialogElement != null) { |
|
|
|
|
return new WixDialog(this, dialogElement, new WixBinaries(this, reader)); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks the file extension to see if the file is a Wix file. The file
|
|
|
|
|
/// can either be a Wix source file (.wxs) or a Wix include file (.wxi).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool IsWixFileName(string fileName) |
|
|
|
|
{ |
|
|
|
|
if (fileName == null) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
string extension = Path.GetExtension(fileName.ToLowerInvariant()); |
|
|
|
|
switch (extension) { |
|
|
|
|
case WixSourceFileExtension: |
|
|
|
|
return true; |
|
|
|
|
case WixIncludeFileExtension: |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks whether the file extension is for a Wix source file (.wxs).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool IsWixSourceFileName(string fileName) |
|
|
|
|
{ |
|
|
|
|
return String.Compare(Path.GetExtension(fileName), WixSourceFileExtension, true) == 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts a DomRegion to an ISegment for the given document.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static ISegment ConvertRegionToSegment(IDocument document, DomRegion region) |
|
|
|
|
XmlElement GetDialogElement(string id) |
|
|
|
|
{ |
|
|
|
|
// Single line region
|
|
|
|
|
if (region.BeginLine == region.EndLine) { |
|
|
|
|
ICSharpCode.TextEditor.Document.LineSegment documentSegment = document.GetLineSegment(region.BeginLine); |
|
|
|
|
return new WixDocumentLineSegment(documentSegment.Offset + region.BeginColumn, |
|
|
|
|
region.EndColumn + 1 - region.BeginColumn); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Multiple line region.
|
|
|
|
|
int length = 0; |
|
|
|
|
int startOffset = 0; |
|
|
|
|
for (int line = region.BeginLine; line <= region.EndLine; ++line) { |
|
|
|
|
ICSharpCode.TextEditor.Document.LineSegment currentSegment = document.GetLineSegment(line); |
|
|
|
|
if (line == region.BeginLine) { |
|
|
|
|
length += currentSegment.TotalLength - region.BeginColumn; |
|
|
|
|
startOffset = currentSegment.Offset + region.BeginColumn; |
|
|
|
|
} else if (line < region.EndLine) { |
|
|
|
|
length += currentSegment.TotalLength; |
|
|
|
|
} else { |
|
|
|
|
length += region.EndColumn + 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return new WixDocumentLineSegment(startOffset, length); |
|
|
|
|
string xpath = GetXPath("//w:Dialog[@Id='{0}']", id); |
|
|
|
|
return (XmlElement)SelectSingleElement(xpath); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates an XML fragment string for the specified element.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="lineTerminator">The line terminator.</param>
|
|
|
|
|
/// <param name="tabsToSpaces">Indicates whether tabs will be converted to spaces.</param>
|
|
|
|
|
/// <param name="tabIndent">The indent to be used when converting tabs to spaces.</param>
|
|
|
|
|
/// <returns>An XML fragment string without any Wix namespace attributes.</returns>
|
|
|
|
|
public static string GetXml(XmlElement element, string lineTerminator, bool tabsToSpaces, int tabIndent) |
|
|
|
|
string GetXPath(string xpathFormat, string arg) |
|
|
|
|
{ |
|
|
|
|
StringBuilder xml = new StringBuilder(); |
|
|
|
|
StringWriter stringWriter = new StringWriter(xml); |
|
|
|
|
XmlWriterSettings xmlWriterSettings = CreateXmlWriterSettings(lineTerminator, tabsToSpaces, tabIndent); |
|
|
|
|
using (XmlWriter xmlWriter = XmlTextWriter.Create(stringWriter, xmlWriterSettings)) { |
|
|
|
|
element.WriteTo(xmlWriter); |
|
|
|
|
} |
|
|
|
|
return xml.ToString().Replace(String.Concat(" xmlns=\"", WixNamespaceManager.Namespace, "\""), String.Empty);; |
|
|
|
|
return String.Format(xpathFormat, XmlEncode(arg)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the region (start line, column to end line, column) of the xml
|
|
|
|
|
/// element that has the specified id. This includes the start and end tags, the start column is the column
|
|
|
|
|
/// containing the start tag and the end column is the column containing the final
|
|
|
|
|
/// end tag marker.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The name of the element.</param>
|
|
|
|
|
/// <param name="id">The id attribute value of the element.</param>
|
|
|
|
|
public static DomRegion GetElementRegion(TextReader reader, string name, string id) |
|
|
|
|
string GetXPath(string xpathFormat, string arg1, string arg2) |
|
|
|
|
{ |
|
|
|
|
XmlTextReader xmlTextReader = new XmlTextReader(reader); |
|
|
|
|
return GetElementRegion(xmlTextReader, name, id); |
|
|
|
|
return String.Format(xpathFormat, XmlEncode(arg1), XmlEncode(arg2)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the region (start line, column to end line, column) of the xml
|
|
|
|
|
/// element that has the specified id. This includes the start and end tags, the start column is the column
|
|
|
|
|
/// containing the start tag and the end column is the column containing the final
|
|
|
|
|
/// end tag marker.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The name of the element.</param>
|
|
|
|
|
/// <param name="id">The id attribute value of the element.</param>
|
|
|
|
|
public static DomRegion GetElementRegion(XmlTextReader reader, string name, string id) |
|
|
|
|
string XmlEncode(string item) |
|
|
|
|
{ |
|
|
|
|
Location startLocation = Location.Empty; |
|
|
|
|
|
|
|
|
|
using (reader) { |
|
|
|
|
int nestedElementsCount = -1; |
|
|
|
|
object elementName = reader.NameTable.Add(name); |
|
|
|
|
while (reader.Read()) { |
|
|
|
|
switch (reader.NodeType) { |
|
|
|
|
case XmlNodeType.Element: |
|
|
|
|
if (IsElementMatch(elementName, reader.LocalName)) { |
|
|
|
|
if (nestedElementsCount == -1) { |
|
|
|
|
bool isEmptyElement = reader.IsEmptyElement; |
|
|
|
|
startLocation = GetStartElementLocationIfMatch(reader, id); |
|
|
|
|
if (!startLocation.IsEmpty) { |
|
|
|
|
nestedElementsCount = 0; |
|
|
|
|
if (isEmptyElement) { |
|
|
|
|
Location endLocation = GetEmptyElementEnd(reader); |
|
|
|
|
return DomRegion.FromLocation(startLocation, endLocation); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else if (!reader.IsEmptyElement) { |
|
|
|
|
++nestedElementsCount; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case XmlNodeType.EndElement: |
|
|
|
|
if (!startLocation.IsEmpty && IsElementMatch(elementName, reader.LocalName)) { |
|
|
|
|
if (nestedElementsCount == 0) { |
|
|
|
|
Location endLocation = GetEndElementEnd(reader); |
|
|
|
|
return DomRegion.FromLocation(startLocation, endLocation); |
|
|
|
|
} |
|
|
|
|
--nestedElementsCount; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return DomRegion.Empty; |
|
|
|
|
char quoteChar = '\''; |
|
|
|
|
return XmlEncoder.Encode(item, quoteChar); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a WixDialog object for the specified dialog id.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">The id of the dialog.</param>
|
|
|
|
|
/// <returns>A <see cref="WixDialog"/> for the dialog id
|
|
|
|
|
/// found; otherwise <see langword="null"/> if no dialog
|
|
|
|
|
/// with the specified id can be found.</returns>
|
|
|
|
|
public WixDialog GetDialog(string id) |
|
|
|
|
XmlElement SelectSingleElement(string xpath) |
|
|
|
|
{ |
|
|
|
|
XmlElement dialogElement = GetDialogElement(id); |
|
|
|
|
if (dialogElement != null) { |
|
|
|
|
return new WixDialog(this, dialogElement); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
return (XmlElement)SelectSingleNode(xpath, namespaceManager); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a WixDialog object for the specified dialog id.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">The id of the dialog.</param>
|
|
|
|
|
/// <param name="reader">The text file reader to use when looking up
|
|
|
|
|
/// binary filenames.</param>
|
|
|
|
|
/// <returns>A <see cref="WixDialog"/> for the dialog id
|
|
|
|
|
/// found; otherwise <see langword="null"/> if no dialog
|
|
|
|
|
/// with the specified id can be found.</returns>
|
|
|
|
|
public WixDialog GetDialog(string id, ITextFileReader reader) |
|
|
|
|
public Bitmap LoadBitmapWithId(string id) |
|
|
|
|
{ |
|
|
|
|
XmlElement dialogElement = GetDialogElement(id); |
|
|
|
|
if (dialogElement != null) { |
|
|
|
|
return new WixDialog(this, dialogElement, new WixBinaries(this, reader)); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
string bitmapFileName = GetBinaryFileName(id); |
|
|
|
|
return LoadBitmapWithFileName(bitmapFileName); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the binary filename for the specified id.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns><see langword="null"/> if the id cannot be found.</returns>
|
|
|
|
|
public string GetBinaryFileName(string id) |
|
|
|
|
{ |
|
|
|
|
string xpath = String.Concat("//w:Binary[@Id='", XmlEncode(id), "']"); |
|
|
|
|
WixBinaryElement binaryElement = (WixBinaryElement)SelectSingleNode(xpath, namespaceManager); |
|
|
|
|
string xpath = GetXPath("//w:Binary[@Id='{0}']", id); |
|
|
|
|
WixBinaryElement binaryElement = (WixBinaryElement)SelectSingleElement(xpath); |
|
|
|
|
if (binaryElement != null) { |
|
|
|
|
return binaryElement.FileName; |
|
|
|
|
return binaryElement.GetFileName(); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the bitmap using the Wix Bitmap id.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Bitmap GetBitmapFromId(string id) |
|
|
|
|
{ |
|
|
|
|
string bitmapFileName = GetBinaryFileName(id); |
|
|
|
|
return GetBitmapFromFileName(bitmapFileName); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the bitmap.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Bitmap GetBitmapFromFileName(string fileName) |
|
|
|
|
public Bitmap LoadBitmapWithFileName(string fileName) |
|
|
|
|
{ |
|
|
|
|
if (fileName != null) { |
|
|
|
|
return fileLoader.GetBitmap(fileName); |
|
|
|
|
return fileLoader.LoadBitmap(fileName); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
@ -506,24 +132,20 @@ namespace ICSharpCode.WixBinding
@@ -506,24 +132,20 @@ namespace ICSharpCode.WixBinding
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a property value defined in the Wix document.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The property value if it is found; an empty string otherwise.</returns>
|
|
|
|
|
public string GetProperty(string name) |
|
|
|
|
{ |
|
|
|
|
string xpath = String.Concat("//w:Property[@Id='", XmlEncode(name), "']"); |
|
|
|
|
XmlElement textStyleElement = (XmlElement)SelectSingleNode(xpath, namespaceManager); |
|
|
|
|
string xpath = GetXPath("//w:Property[@Id='{0}']", name); |
|
|
|
|
XmlElement textStyleElement = SelectSingleElement(xpath); |
|
|
|
|
if (textStyleElement != null) { |
|
|
|
|
return textStyleElement.InnerText; |
|
|
|
|
} |
|
|
|
|
return String.Empty; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the preprocessor variable value from the WixProject.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string GetValue(string name) |
|
|
|
|
string IWixPropertyValueProvider.GetValue(string name) |
|
|
|
|
{ |
|
|
|
|
if (project != null) { |
|
|
|
|
return project.GetVariable(name); |
|
|
|
|
return project.GetPreprocessorVariableValue(name); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
@ -531,100 +153,59 @@ namespace ICSharpCode.WixBinding
@@ -531,100 +153,59 @@ namespace ICSharpCode.WixBinding
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the top SOURCEDIR directory.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public WixDirectoryElement RootDirectory { |
|
|
|
|
get { |
|
|
|
|
string xpath = String.Concat("//w:Product/w:Directory[@Id='", WixDirectoryElement.RootDirectoryId, "']"); |
|
|
|
|
return (WixDirectoryElement)SelectSingleNode(xpath, namespaceManager); |
|
|
|
|
} |
|
|
|
|
public WixDirectoryElement GetRootDirectory() |
|
|
|
|
{ |
|
|
|
|
string xpath = GetXPath("//w:Product/w:Directory[@Id='{0}']", WixDirectoryElement.RootDirectoryId); |
|
|
|
|
return (WixDirectoryElement)SelectSingleElement(xpath); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a reference to the root directory.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public WixDirectoryRefElement RootDirectoryRef { |
|
|
|
|
get { |
|
|
|
|
string xpath = String.Concat("//w:DirectoryRef[@Id='", WixDirectoryElement.RootDirectoryId, "']"); |
|
|
|
|
return (WixDirectoryRefElement)SelectSingleNode(xpath, namespaceManager); |
|
|
|
|
} |
|
|
|
|
public WixDirectoryRefElement GetRootDirectoryRef() |
|
|
|
|
{ |
|
|
|
|
string xpath = GetXPath("//w:DirectoryRef[@Id='{0}']", WixDirectoryElement.RootDirectoryId); |
|
|
|
|
return (WixDirectoryRefElement)SelectSingleElement(xpath); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Wix document contains the Product element.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsProductDocument { |
|
|
|
|
get { |
|
|
|
|
return Product != null; |
|
|
|
|
} |
|
|
|
|
public bool HasProduct { |
|
|
|
|
get { return GetProduct() != null; } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the prefix used for the Wix namespace.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string WixNamespacePrefix { |
|
|
|
|
get { |
|
|
|
|
XmlElement documentElement = DocumentElement; |
|
|
|
|
if (documentElement != null) { |
|
|
|
|
return documentElement.GetPrefixOfNamespace(WixNamespaceManager.Namespace); |
|
|
|
|
} |
|
|
|
|
return String.Empty; |
|
|
|
|
public string GetWixNamespacePrefix() |
|
|
|
|
{ |
|
|
|
|
XmlElement documentElement = DocumentElement; |
|
|
|
|
if (documentElement != null) { |
|
|
|
|
return documentElement.GetPrefixOfNamespace(WixNamespaceManager.Namespace); |
|
|
|
|
} |
|
|
|
|
return String.Empty; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds a new root directory.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public WixDirectoryElement AddRootDirectory() |
|
|
|
|
{ |
|
|
|
|
// Add product element if it does not exist.
|
|
|
|
|
XmlElement productElement = Product; |
|
|
|
|
XmlElement productElement = GetProduct(); |
|
|
|
|
if (productElement == null) { |
|
|
|
|
productElement = CreateWixElement("Product"); |
|
|
|
|
DocumentElement.AppendChild(productElement); |
|
|
|
|
productElement = AddProduct(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Add root directory.
|
|
|
|
|
WixDirectoryElement rootDirectory = WixDirectoryElement.CreateRootDirectory(this); |
|
|
|
|
return (WixDirectoryElement)productElement.AppendChild(rootDirectory); |
|
|
|
|
return AddRootDirectoryToProduct(productElement); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a new Xml element belonging to the Wix namespace.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public XmlElement CreateWixElement(string name) |
|
|
|
|
XmlElement AddProduct() |
|
|
|
|
{ |
|
|
|
|
return CreateElement(WixNamespacePrefix, name, WixNamespaceManager.Namespace); |
|
|
|
|
XmlElement productElement = CreateWixElement("Product"); |
|
|
|
|
DocumentElement.AppendChild(productElement); |
|
|
|
|
return productElement; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Saves the document to the location specified by WixDocument.FileName.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void Save(string lineTerminator, bool tabsToSpaces, int tabIndent) |
|
|
|
|
WixDirectoryElement AddRootDirectoryToProduct(XmlElement parentProductElement) |
|
|
|
|
{ |
|
|
|
|
XmlWriterSettings xmlWriterSettings = CreateXmlWriterSettings(lineTerminator, tabsToSpaces, tabIndent); |
|
|
|
|
using (XmlWriter xmlWriter = XmlTextWriter.Create(fileName, xmlWriterSettings)) { |
|
|
|
|
Save(xmlWriter); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the Product element.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public XmlElement Product { |
|
|
|
|
get { |
|
|
|
|
return (XmlElement)SelectSingleNode("w:Wix/w:Product", namespaceManager); |
|
|
|
|
} |
|
|
|
|
WixDirectoryElement rootDirectory = WixDirectoryElement.CreateRootDirectory(this); |
|
|
|
|
return (WixDirectoryElement)parentProductElement.AppendChild(rootDirectory); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the binary elements defined in this document.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public WixBinaryElement[] GetBinaries() |
|
|
|
|
public XmlElement CreateWixElement(string name) |
|
|
|
|
{ |
|
|
|
|
List<WixBinaryElement> binaries = new List<WixBinaryElement>(); |
|
|
|
|
foreach (WixBinaryElement element in SelectNodes("//w:Binary", namespaceManager)) { |
|
|
|
|
binaries.Add(element); |
|
|
|
|
} |
|
|
|
|
return binaries.ToArray(); |
|
|
|
|
return CreateElement(GetWixNamespacePrefix(), name, WixNamespaceManager.Namespace); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -644,6 +225,8 @@ namespace ICSharpCode.WixBinding
@@ -644,6 +225,8 @@ namespace ICSharpCode.WixBinding
|
|
|
|
|
return new WixDirectoryRefElement(this); |
|
|
|
|
case WixBinaryElement.BinaryElementName: |
|
|
|
|
return new WixBinaryElement(this); |
|
|
|
|
case WixDialogElement.DialogElementName: |
|
|
|
|
return new WixDialogElement(this); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return base.CreateElement(prefix, localName, namespaceURI); |
|
|
|
@ -658,6 +241,13 @@ namespace ICSharpCode.WixBinding
@@ -658,6 +241,13 @@ namespace ICSharpCode.WixBinding
|
|
|
|
|
return ElementIdExists(WixFileElement.FileElementName, id); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool ElementIdExists(string elementName, string id) |
|
|
|
|
{ |
|
|
|
|
string xpath = GetXPath("//w:{0}[@Id='{1}']", elementName, id); |
|
|
|
|
XmlNodeList nodes = SelectNodes(xpath, new WixNamespaceManager(NameTable)); |
|
|
|
|
return nodes.Count > 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks to see if a Component element exists with the specified id in this
|
|
|
|
|
/// document.
|
|
|
|
@ -673,7 +263,7 @@ namespace ICSharpCode.WixBinding
@@ -673,7 +263,7 @@ namespace ICSharpCode.WixBinding
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string GetFullPath(string relativePath) |
|
|
|
|
{ |
|
|
|
|
if (fileName != null && fileName.Length > 0) { |
|
|
|
|
if (!String.IsNullOrEmpty(fileName)) { |
|
|
|
|
string basePath = Path.GetDirectoryName(fileName); |
|
|
|
|
return FileUtility.GetAbsolutePath(basePath, relativePath); |
|
|
|
|
} |
|
|
|
@ -686,149 +276,25 @@ namespace ICSharpCode.WixBinding
@@ -686,149 +276,25 @@ namespace ICSharpCode.WixBinding
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string GetRelativePath(string fullPath) |
|
|
|
|
{ |
|
|
|
|
if (fileName != null && fileName.Length > 0) { |
|
|
|
|
if (!String.IsNullOrEmpty(fileName)) { |
|
|
|
|
string basePath = Path.GetDirectoryName(fileName); |
|
|
|
|
return FileUtility.GetRelativePath(basePath, fullPath); |
|
|
|
|
} |
|
|
|
|
return fullPath; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Reads the dialog id and adds it to the list of dialogs found so far.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="reader">An XmlReader which is currently at the dialog start element.</param>
|
|
|
|
|
static void AddDialogId(XmlReader reader, List<string> dialogIds) |
|
|
|
|
public XmlElement GetProduct() |
|
|
|
|
{ |
|
|
|
|
string id = GetIdFromCurrentNode(reader); |
|
|
|
|
if (id.Length > 0) { |
|
|
|
|
dialogIds.Add(id); |
|
|
|
|
} |
|
|
|
|
return SelectSingleElement("w:Wix/w:Product"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the id for the current element.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="reader">An XmlReader which is currently at the start element.</param>
|
|
|
|
|
static string GetIdFromCurrentNode(XmlReader reader) |
|
|
|
|
{ |
|
|
|
|
if (reader.MoveToAttribute("Id")) { |
|
|
|
|
if (reader.Value != null) { |
|
|
|
|
return reader.Value; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return String.Empty; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks that the atomised element names match.
|
|
|
|
|
/// </summary>
|
|
|
|
|
static bool IsElementMatch(object elementName, object currentElementName) |
|
|
|
|
{ |
|
|
|
|
return elementName == currentElementName; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the line and column position if the dialog id matches the dialog
|
|
|
|
|
/// element at the current reader position. This returns the column for the
|
|
|
|
|
/// starting less than start tag.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id">A dialog id.</param>
|
|
|
|
|
/// <param name="reader">An XmlTextReader currently at the dialog start element.</param>
|
|
|
|
|
/// <returns><see langword="null"/> if the dialog id does not match.</returns>
|
|
|
|
|
static Location GetStartElementLocationIfMatch(XmlTextReader reader, string id) |
|
|
|
|
{ |
|
|
|
|
// Store the column and line position since the call to GetDialogId will
|
|
|
|
|
// move to the <Dialog> Id attribute.
|
|
|
|
|
int line = reader.LineNumber - 1; |
|
|
|
|
int column = reader.LinePosition - 2; // Take off 2 to so the '<' is located.
|
|
|
|
|
|
|
|
|
|
if (id == GetIdFromCurrentNode(reader)) { |
|
|
|
|
return new Location(column, line); |
|
|
|
|
} |
|
|
|
|
return Location.Empty; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines the end of the end element including the final end tag marker
|
|
|
|
|
/// (the greater than sign). This method moves the XmlTextReader to the end of
|
|
|
|
|
/// the end tag.
|
|
|
|
|
/// </summary>
|
|
|
|
|
static Location GetEndElementEnd(XmlTextReader reader) |
|
|
|
|
{ |
|
|
|
|
reader.ReadEndElement(); |
|
|
|
|
int line = reader.LineNumber - 1; |
|
|
|
|
|
|
|
|
|
// Take off two as we have moved passed the end tag column.
|
|
|
|
|
int column = reader.LinePosition - 2; |
|
|
|
|
|
|
|
|
|
// If ReadEndElement has moved to the start of another element
|
|
|
|
|
// take off one from the column value otherwise the column
|
|
|
|
|
// value includes the start tag of the next element.
|
|
|
|
|
if (reader.NodeType == XmlNodeType.Element) { |
|
|
|
|
--column; |
|
|
|
|
} |
|
|
|
|
return new Location(column, line); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines the end of the empty element including the final end tag marker
|
|
|
|
|
/// (the greater than sign). This method moves the XmlTextReader to the end
|
|
|
|
|
/// of the element tag.
|
|
|
|
|
/// </summary>
|
|
|
|
|
static Location GetEmptyElementEnd(XmlTextReader reader) |
|
|
|
|
{ |
|
|
|
|
reader.ReadStartElement(); |
|
|
|
|
int line = reader.LineNumber - 1; |
|
|
|
|
|
|
|
|
|
// Take off two as we have moved passed the end tag
|
|
|
|
|
// column.
|
|
|
|
|
int column = reader.LinePosition - 2; |
|
|
|
|
return new Location(column, line); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates an XmlWriterSettings based on the text editor properties.
|
|
|
|
|
/// </summary>
|
|
|
|
|
static XmlWriterSettings CreateXmlWriterSettings(string lineTerminator, bool tabsToSpaces, int tabIndent) |
|
|
|
|
public WixBinaryElement[] GetBinaries() |
|
|
|
|
{ |
|
|
|
|
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings(); |
|
|
|
|
xmlWriterSettings.CloseOutput = true; |
|
|
|
|
xmlWriterSettings.Indent = true; |
|
|
|
|
xmlWriterSettings.NewLineChars = lineTerminator; |
|
|
|
|
xmlWriterSettings.OmitXmlDeclaration = true; |
|
|
|
|
|
|
|
|
|
if (tabsToSpaces) { |
|
|
|
|
string spaces = " "; |
|
|
|
|
xmlWriterSettings.IndentChars = spaces.PadRight(tabIndent); |
|
|
|
|
} else { |
|
|
|
|
xmlWriterSettings.IndentChars = "\t"; |
|
|
|
|
List<WixBinaryElement> binaries = new List<WixBinaryElement>(); |
|
|
|
|
foreach (WixBinaryElement element in SelectNodes("//w:Binary", namespaceManager)) { |
|
|
|
|
binaries.Add(element); |
|
|
|
|
} |
|
|
|
|
return xmlWriterSettings; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the dialog element with the specified id.
|
|
|
|
|
/// </summary>
|
|
|
|
|
XmlElement GetDialogElement(string id) |
|
|
|
|
{ |
|
|
|
|
string xpath = String.Concat("//w:Dialog[@Id='", XmlEncode(id), "']"); |
|
|
|
|
return (XmlElement)SelectSingleNode(xpath, namespaceManager); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks to see if an element exists with the specified Id attribute.
|
|
|
|
|
/// </summary>
|
|
|
|
|
bool ElementIdExists(string elementName, string id) |
|
|
|
|
{ |
|
|
|
|
string xpath = String.Concat("//w:", elementName, "[@Id='", XmlEncode(id), "']"); |
|
|
|
|
XmlNodeList nodes = SelectNodes(xpath, new WixNamespaceManager(NameTable)); |
|
|
|
|
return nodes.Count > 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static string XmlEncode(string item) |
|
|
|
|
{ |
|
|
|
|
char quoteChar = '\''; |
|
|
|
|
return XmlEncoder.Encode(item, quoteChar); |
|
|
|
|
return binaries.ToArray(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|