// 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.Xml.Linq;
using WixToolset.Data;
using WixToolset.Data.Tuples;
using WixToolset.Extensibility;
///
/// Compiler of the WiX toolset.
///
internal partial class Compiler : ICompiler
{
///
/// Parses a patch creation element.
///
/// The element to parse.
private void ParsePatchCreationElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var clean = true; // Default is to clean
var codepage = 0;
string outputPath = null;
var productMismatches = false;
var replaceGuids = String.Empty;
string sourceList = null;
string symbolFlags = null;
var targetProducts = String.Empty;
var versionMismatches = false;
var wholeFiles = false;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
this.activeName = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
break;
case "AllowMajorVersionMismatches":
versionMismatches = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "AllowProductCodeMismatches":
productMismatches = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "CleanWorkingFolder":
clean = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Codepage":
codepage = this.Core.GetAttributeCodePageValue(sourceLineNumbers, attrib);
break;
case "OutputPath":
outputPath = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "SourceList":
sourceList = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "SymbolFlags":
symbolFlags = String.Format(CultureInfo.InvariantCulture, "0x{0:x8}", this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, 0, UInt32.MaxValue));
break;
case "WholeFilesOnly":
wholeFiles = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == this.activeName)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
this.Core.CreateActiveSection(this.activeName, SectionType.PatchCreation, codepage, this.Context.CompilationId);
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "Family":
this.ParseFamilyElement(child);
break;
case "PatchInformation":
this.ParsePatchInformationElement(child);
break;
case "PatchMetadata":
this.ParsePatchMetadataElement(child);
break;
case "PatchProperty":
this.ParsePatchPropertyElement(child, false);
break;
case "PatchSequence":
this.ParsePatchSequenceElement(child);
break;
case "ReplacePatch":
replaceGuids = String.Concat(replaceGuids, this.ParseReplacePatchElement(child));
break;
case "TargetProductCode":
var targetProduct = this.ParseTargetProductCodeElement(child);
if (0 < targetProducts.Length)
{
targetProducts = String.Concat(targetProducts, ";");
}
targetProducts = String.Concat(targetProducts, targetProduct);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
this.AddPrivateProperty(sourceLineNumbers, "PatchGUID", this.activeName);
this.AddPrivateProperty(sourceLineNumbers, "AllowProductCodeMismatches", productMismatches ? "1" : "0");
this.AddPrivateProperty(sourceLineNumbers, "AllowProductVersionMajorMismatches", versionMismatches ? "1" : "0");
this.AddPrivateProperty(sourceLineNumbers, "DontRemoveTempFolderWhenFinished", clean ? "0" : "1");
this.AddPrivateProperty(sourceLineNumbers, "IncludeWholeFilesOnly", wholeFiles ? "1" : "0");
if (null != symbolFlags)
{
this.AddPrivateProperty(sourceLineNumbers, "ApiPatchingSymbolFlags", symbolFlags);
}
if (0 < replaceGuids.Length)
{
this.AddPrivateProperty(sourceLineNumbers, "ListOfPatchGUIDsToReplace", replaceGuids);
}
if (0 < targetProducts.Length)
{
this.AddPrivateProperty(sourceLineNumbers, "ListOfTargetProductCodes", targetProducts);
}
if (null != outputPath)
{
this.AddPrivateProperty(sourceLineNumbers, "PatchOutputPath", outputPath);
}
if (null != sourceList)
{
this.AddPrivateProperty(sourceLineNumbers, "PatchSourceList", sourceList);
}
}
///
/// Parses a family element.
///
/// The element to parse.
private void ParseFamilyElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var diskId = CompilerConstants.IntegerNotSet;
string diskPrompt = null;
string mediaSrcProp = null;
string name = null;
var sequenceStart = CompilerConstants.IntegerNotSet;
string volumeLabel = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "DiskId":
diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue);
break;
case "DiskPrompt":
diskPrompt = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "MediaSrcProp":
mediaSrcProp = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Name":
name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "SequenceStart":
sequenceStart = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int32.MaxValue);
break;
case "VolumeLabel":
volumeLabel = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == name)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
}
else if (0 < name.Length)
{
if (8 < name.Length) // check the length
{
this.Core.Write(ErrorMessages.FamilyNameTooLong(sourceLineNumbers, node.Name.LocalName, "Name", name, name.Length));
}
else // check for illegal characters
{
foreach (var character in name)
{
if (!Char.IsLetterOrDigit(character) && '_' != character)
{
this.Core.Write(ErrorMessages.IllegalFamilyName(sourceLineNumbers, node.Name.LocalName, "Name", name));
}
}
}
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "UpgradeImage":
this.ParseUpgradeImageElement(child, name);
break;
case "ExternalFile":
this.ParseExternalFileElement(child, name);
break;
case "ProtectFile":
this.ParseProtectFileElement(child, name);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
var tuple = new ImageFamiliesTuple(sourceLineNumbers)
{
Family = name,
MediaSrcPropName = mediaSrcProp,
DiskPrompt = diskPrompt,
VolumeLabel = volumeLabel
};
if (CompilerConstants.IntegerNotSet != diskId)
{
tuple.MediaDiskId = diskId;
}
if (CompilerConstants.IntegerNotSet != sequenceStart)
{
tuple.FileSequenceStart = sequenceStart;
}
this.Core.AddTuple(tuple);
}
}
///
/// Parses an upgrade image element.
///
/// The element to parse.
/// The family for this element.
private void ParseUpgradeImageElement(XElement node, string family)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string sourceFile = null;
string sourcePatch = null;
var symbols = new List();
string upgrade = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
upgrade = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
if (13 < upgrade.Length)
{
this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", upgrade, 13));
}
break;
case "SourceFile":
case "src":
if (null != sourceFile)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "SourceFile"));
}
if ("src" == attrib.Name.LocalName)
{
this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourceFile"));
}
sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "SourcePatch":
case "srcPatch":
if (null != sourcePatch)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "srcPatch", "SourcePatch"));
}
if ("srcPatch" == attrib.Name.LocalName)
{
this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourcePatch"));
}
sourcePatch = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == upgrade)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
if (null == sourceFile)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "SymbolPath":
symbols.Add(this.ParseSymbolPathElement(child));
break;
case "TargetImage":
this.ParseTargetImageElement(child, upgrade, family);
break;
case "UpgradeFile":
this.ParseUpgradeFileElement(child, upgrade);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
this.Core.AddTuple(new UpgradedImagesTuple(sourceLineNumbers)
{
Upgraded = upgrade,
MsiPath = sourceFile,
PatchMsiPath = sourcePatch,
SymbolPaths = String.Join(";", symbols),
Family = family
});
}
}
///
/// Parses an upgrade file element.
///
/// The element to parse.
/// The upgrade key for this element.
private void ParseUpgradeFileElement(XElement node, string upgrade)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var allowIgnoreOnError = false;
string file = null;
var ignore = false;
var symbols = new List();
var wholeFile = false;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "AllowIgnoreOnError":
allowIgnoreOnError = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "File":
file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Ignore":
ignore = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "WholeFile":
wholeFile = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == file)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "SymbolPath":
symbols.Add(this.ParseSymbolPathElement(child));
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
if (ignore)
{
this.Core.AddTuple(new UpgradedFilesToIgnoreTuple(sourceLineNumbers)
{
Upgraded = upgrade,
FTK = file
});
}
else
{
this.Core.AddTuple(new UpgradedFilesOptionalDataTuple(sourceLineNumbers)
{
Upgraded = upgrade,
FTK = file,
SymbolPaths = String.Join(";", symbols),
AllowIgnoreOnPatchError = allowIgnoreOnError,
IncludeWholeFile = wholeFile
});
}
}
}
///
/// Parses a target image element.
///
/// The element to parse.
/// The upgrade key for this element.
/// The family key for this element.
private void ParseTargetImageElement(XElement node, string upgrade, string family)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var ignore = false;
var order = CompilerConstants.IntegerNotSet;
string sourceFile = null;
string symbols = null;
string target = null;
string validation = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
if (target.Length > 13)
{
this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", target, 13));
}
break;
case "IgnoreMissingFiles":
ignore = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Order":
order = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue);
break;
case "SourceFile":
case "src":
if (null != sourceFile)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "SourceFile"));
}
if ("src" == attrib.Name.LocalName)
{
this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourceFile"));
}
sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Validation":
validation = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == target)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
if (null == sourceFile)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
}
if (CompilerConstants.IntegerNotSet == order)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Order"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "SymbolPath":
if (null != symbols)
{
symbols = String.Concat(symbols, ";", this.ParseSymbolPathElement(child));
}
else
{
symbols = this.ParseSymbolPathElement(child);
}
break;
case "TargetFile":
this.ParseTargetFileElement(child, target, family);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
this.Core.AddTuple(new TargetImagesTuple(sourceLineNumbers)
{
Target = target,
MsiPath = sourceFile,
SymbolPaths = symbols,
Upgraded = upgrade,
Order = order,
ProductValidateFlags = validation,
IgnoreMissingSrcFiles = ignore
});
}
}
///
/// Parses an upgrade file element.
///
/// The element to parse.
/// The upgrade key for this element.
/// The family key for this element.
private void ParseTargetFileElement(XElement node, string target, string family)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string file = null;
string ignoreLengths = null;
string ignoreOffsets = null;
string protectLengths = null;
string protectOffsets = null;
string symbols = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == file)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "IgnoreRange":
this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
break;
case "ProtectRange":
this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
break;
case "SymbolPath":
symbols = this.ParseSymbolPathElement(child);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
var tuple = new TargetFilesOptionalDataTuple(sourceLineNumbers)
{
Target = target,
FTK = file,
SymbolPaths = symbols,
IgnoreOffsets = ignoreOffsets,
IgnoreLengths = ignoreLengths
};
this.Core.AddTuple(tuple);
if (null != protectOffsets)
{
tuple.RetainOffsets = protectOffsets;
this.Core.AddTuple(new FamilyFileRangesTuple(sourceLineNumbers)
{
Family = family,
FTK = file,
RetainOffsets = protectOffsets,
RetainLengths = protectLengths
});
}
}
}
///
/// Parses an external file element.
///
/// The element to parse.
/// The family for this element.
private void ParseExternalFileElement(XElement node, string family)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string file = null;
string ignoreLengths = null;
string ignoreOffsets = null;
var order = CompilerConstants.IntegerNotSet;
string protectLengths = null;
string protectOffsets = null;
string source = null;
string symbols = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "File":
file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Order":
order = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue);
break;
case "Source":
case "src":
if (null != source)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "Source"));
}
if ("src" == attrib.Name.LocalName)
{
this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Source"));
}
source = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == file)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
}
if (null == source)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Source"));
}
if (CompilerConstants.IntegerNotSet == order)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Order"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "IgnoreRange":
this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
break;
case "ProtectRange":
this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
break;
case "SymbolPath":
symbols = this.ParseSymbolPathElement(child);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
var tuple = new ExternalFilesTuple(sourceLineNumbers)
{
Family = family,
FTK = file,
FilePath = source,
SymbolPaths = symbols,
IgnoreOffsets = ignoreOffsets,
IgnoreLengths = ignoreLengths
};
if (null != protectOffsets)
{
tuple.RetainOffsets = protectOffsets;
}
if (CompilerConstants.IntegerNotSet != order)
{
tuple.Order = order;
}
this.Core.AddTuple(tuple);
if (null != protectOffsets)
{
this.Core.AddTuple(new FamilyFileRangesTuple(sourceLineNumbers)
{
Family = family,
FTK = file,
RetainOffsets = protectOffsets,
RetainLengths = protectLengths
});
}
}
}
///
/// Parses a protect file element.
///
/// The element to parse.
/// The family for this element.
private void ParseProtectFileElement(XElement node, string family)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string file = null;
string protectLengths = null;
string protectOffsets = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "File":
file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == file)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "ProtectRange":
this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (null == protectOffsets || null == protectLengths)
{
this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "ProtectRange"));
}
if (!this.Core.EncounteredError)
{
this.Core.AddTuple(new FamilyFileRangesTuple(sourceLineNumbers)
{
Family = family,
FTK = file,
RetainOffsets = protectOffsets,
RetainLengths = protectLengths
});
}
}
///
/// Parses a range element (ProtectRange, IgnoreRange, etc).
///
/// The element to parse.
/// Reference to the offsets string.
/// Reference to the lengths string.
private void ParseRangeElement(XElement node, ref string offsets, ref string lengths)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string length = null;
string offset = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Length":
length = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Offset":
offset = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == length)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Length"));
}
if (null == offset)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Offset"));
}
this.Core.ParseForExtensionElements(node);
if (null != lengths)
{
lengths = String.Concat(lengths, ",", length);
}
else
{
lengths = length;
}
if (null != offsets)
{
offsets = String.Concat(offsets, ",", offset);
}
else
{
offsets = offset;
}
}
///
/// Parses a patch metadata element.
///
/// Element to parse.
private void ParsePatchMetadataElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var allowRemoval = YesNoType.NotSet;
string classification = null;
string creationTimeUtc = null;
string description = null;
string displayName = null;
string manufacturerName = null;
string minorUpdateTargetRTM = null;
string moreInfoUrl = null;
var optimizeCA = CompilerConstants.IntegerNotSet;
var optimizedInstallMode = YesNoType.NotSet;
string targetProductName = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "AllowRemoval":
allowRemoval = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Classification":
classification = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "CreationTimeUTC":
creationTimeUtc = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Description":
description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "DisplayName":
displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "ManufacturerName":
manufacturerName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "MinorUpdateTargetRTM":
minorUpdateTargetRTM = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "MoreInfoURL":
moreInfoUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "OptimizedInstallMode":
optimizedInstallMode = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "TargetProductName":
targetProductName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (YesNoType.NotSet == allowRemoval)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "AllowRemoval"));
}
if (null == classification)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Classification"));
}
if (null == description)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
}
if (null == displayName)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayName"));
}
if (null == manufacturerName)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ManufacturerName"));
}
if (null == moreInfoUrl)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "MoreInfoURL"));
}
if (null == targetProductName)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "TargetProductName"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "CustomProperty":
this.ParseCustomPropertyElement(child);
break;
case "OptimizeCustomActions":
optimizeCA = this.ParseOptimizeCustomActionsElement(child);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
if (YesNoType.NotSet != allowRemoval)
{
this.AddPatchMetadata(sourceLineNumbers, null, "AllowRemoval", YesNoType.Yes == allowRemoval ? "1" : "0");
}
if (null != classification)
{
this.AddPatchMetadata(sourceLineNumbers, null, "Classification", classification);
}
if (null != creationTimeUtc)
{
this.AddPatchMetadata(sourceLineNumbers, null, "CreationTimeUTC", creationTimeUtc);
}
if (null != description)
{
this.AddPatchMetadata(sourceLineNumbers, null, "Description", description);
}
if (null != displayName)
{
this.AddPatchMetadata(sourceLineNumbers, null, "DisplayName", displayName);
}
if (null != manufacturerName)
{
this.AddPatchMetadata(sourceLineNumbers, null, "ManufacturerName", manufacturerName);
}
if (null != minorUpdateTargetRTM)
{
this.AddPatchMetadata(sourceLineNumbers, null, "MinorUpdateTargetRTM", minorUpdateTargetRTM);
}
if (null != moreInfoUrl)
{
this.AddPatchMetadata(sourceLineNumbers, null, "MoreInfoURL", moreInfoUrl);
}
if (CompilerConstants.IntegerNotSet != optimizeCA)
{
this.AddPatchMetadata(sourceLineNumbers, null, "OptimizeCA", optimizeCA.ToString(CultureInfo.InvariantCulture));
}
if (YesNoType.NotSet != optimizedInstallMode)
{
this.AddPatchMetadata(sourceLineNumbers, null, "OptimizedInstallMode", YesNoType.Yes == optimizedInstallMode ? "1" : "0");
}
if (null != targetProductName)
{
this.AddPatchMetadata(sourceLineNumbers, null, "TargetProductName", targetProductName);
}
}
}
///
/// Parses a custom property element for the PatchMetadata table.
///
/// Element to parse.
private void ParseCustomPropertyElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string company = null;
string property = null;
string value = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Company":
company = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Property":
property = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Value":
value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == company)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Company"));
}
if (null == property)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
}
if (null == value)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.AddPatchMetadata(sourceLineNumbers, company, property, value);
}
}
///
/// Parses a patch sequence element.
///
/// The element to parse.
private void ParsePatchSequenceElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string family = null;
string target = null;
string sequence = null;
var attributes = 0;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "PatchFamily":
family = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
break;
case "ProductCode":
if (null != target)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Target", "TargetImage"));
}
target = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
break;
case "Target":
if (null != target)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "TargetImage", "ProductCode"));
}
this.Core.Write(WarningMessages.DeprecatedPatchSequenceTargetAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "TargetImage":
if (null != target)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Target", "ProductCode"));
}
target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, "TargetImages", target);
break;
case "Sequence":
sequence = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
break;
case "Supersede":
if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
{
attributes |= 0x1;
}
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == family)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "PatchFamily"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var tuple = new PatchSequenceTuple(sourceLineNumbers)
{
PatchFamily = family,
Target = target,
Sequence = sequence,
Supersede = attributes
};
this.Core.AddTuple(tuple);
}
}
private void AddPatchMetadata(SourceLineNumber sourceLineNumbers, string company, string property, string value)
{
this.Core.AddTuple(new PatchMetadataTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, company, property))
{
Company = company,
Property = property,
Value = value
});
}
}
}