public void CreateProtectedDirectoryFromInlineSyntax()
        {
            XDocument doc = XDocument.Parse("<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'><Product Id='*' Language='1033' Manufacturer='WixTests' Name='CreateProtectedDirectoryFromInlineSyntax' Version='1.0.0' UpgradeCode='12345678-1234-1234-1234-1234567890AB'><DirectoryRef Id='BinFolder'/></Product>" +
                                            @"<Fragment><Directory Id='protected BinFolder' Name='TARGETDIR:\foo\bar\bin\' /></Fragment>" +
                                            "<Fragment><Directory Id='TARGETDIR' Name='SourceDir' /></Fragment></Wix>");
            XDocument src      = new Preprocessor().Process(doc.CreateReader(), new Dictionary <string, string>());
            Compiler  compiler = new Compiler();
            Linker    linker   = new Linker();

            Intermediate intermediate = compiler.Compile(src);
            Output       output       = linker.Link(intermediate.Sections, OutputType.Product);

            RowIndexedList <Row> directoryRows = new RowIndexedList <Row>(output.Sections.SelectMany(sec => sec.Tables).Where(t => t.Name.Equals("Directory")).SelectMany(d => d.Rows));

            Assert.Equal(4, directoryRows.Count);

            Row binFolder = directoryRows.Get("BinFolder");
            Row barFolder = directoryRows.Get((string)binFolder[1]);
            Row fooFolder = directoryRows.Get((string)barFolder[1]);
            Row targetDir = directoryRows.Get((string)fooFolder[1]);

            Assert.Equal(AccessModifier.Protected, binFolder.Access);
            Assert.Equal(AccessModifier.Private, barFolder.Access);
            Assert.Equal(AccessModifier.Private, fooFolder.Access);
            Assert.Equal(AccessModifier.Public, targetDir.Access);

            Assert.Equal("bin", binFolder[2]);
            Assert.Equal("bar", barFolder[2]);
            Assert.Equal("foo", fooFolder[2]);
            Assert.Equal("SourceDir", targetDir[2]);
        }
        public void InlineDirectorySyntaxCollapses()
        {
            XDocument doc = XDocument.Parse("<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'><Product Id='*' Language='1033' Manufacturer='WixTests' Name='InlineDirectorySyntaxCollapses' Version='1.0.0' UpgradeCode='12345678-1234-1234-1234-1234567890AB'><DirectoryRef Id='Bin1Folder'/><DirectoryRef Id='Bin2Folder'/></Product>" +
                                            @"<Fragment><Directory Id='protected Bin1Folder' Name='TARGETDIR:\foo\bar\bin1\' /></Fragment>" +
                                            @"<Fragment><Directory Id='internal Bin2Folder' Name='TARGETDIR:\foo\bar\bin2\' /></Fragment>" +
                                            "<Fragment><Directory Id='TARGETDIR' Name='SourceDir' /></Fragment></Wix>");
            XDocument src      = new Preprocessor().Process(doc.CreateReader(), new Dictionary <string, string>());
            Compiler  compiler = new Compiler();
            Linker    linker   = new Linker();

            Intermediate intermediate = compiler.Compile(src);
            Output       output       = linker.Link(intermediate.Sections, OutputType.Product);

            RowIndexedList <Row> directoryRows = new RowIndexedList <Row>(output.Sections.SelectMany(sec => sec.Tables).Where(t => t.Name.Equals("Directory")).SelectMany(d => d.Rows));

            Assert.Equal(7, directoryRows.Count);

            Row bin1Folder = directoryRows.Get("Bin1Folder");
            Row bar1Folder = directoryRows.Get((string)bin1Folder[1]);
            Row foo1Folder = directoryRows.Get((string)bar1Folder[1]);
            Row targetDir  = directoryRows.Get((string)foo1Folder[1]);

            Row bin2Folder = directoryRows.Get("Bin2Folder");
            Row bar2Folder = directoryRows.Get((string)bin2Folder[1]);
            Row foo2Folder = directoryRows.Get((string)bar2Folder[1]);

            Assert.Equal(AccessModifier.Protected, bin1Folder.Access);
            Assert.Equal(AccessModifier.Private, bar1Folder.Access);
            Assert.Equal(AccessModifier.Private, foo1Folder.Access);
            Assert.Equal(AccessModifier.Public, targetDir.Access);

            Assert.Equal(AccessModifier.Internal, bin2Folder.Access);
            Assert.Equal(AccessModifier.Private, bar2Folder.Access);
            Assert.Equal(AccessModifier.Private, foo2Folder.Access);
            Assert.Equal(targetDir, directoryRows.Get((string)foo2Folder[1]));

            // Primary keys should be the same for the inline directories.
            Assert.Equal(bar1Folder[0], bar2Folder[0]);
            Assert.Equal(foo1Folder[0], foo2Folder[0]);

            // One and only one of the same inline directories should be marked redundant.
            foreach (Row duplicate in directoryRows.Duplicates)
            {
                Row nonDupe = directoryRows.Get((string)duplicate[0]);
                Assert.True(nonDupe.Redundant ^ duplicate.Redundant);
                Assert.True(nonDupe.IsIdentical(duplicate));
            }
        }
        public void ComponentUsesInlineDirectorySyntax()
        {
            XDocument doc = XDocument.Parse("<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'><Product Id='*' Language='1033' Manufacturer='WixTests' Name='ComponentUsesInlineDirectorySyntax' Version='1.0.0' UpgradeCode='12345678-1234-1234-1234-1234567890AB'><Feature Id='Feature1'><ComponentRef Id='Component1'/></Feature></Product>" +
                                            @"<Fragment><Component Id='protected Component1' Directory='BinFolder:\comp\' /></Fragment>" +
                                            @"<Fragment><Directory Id='protected BinFolder' Name='TARGETDIR:\foo\bar\bin\' /></Fragment>" +
                                            "<Fragment><Directory Id='TARGETDIR' Name='SourceDir' /></Fragment></Wix>");
            XDocument src      = new Preprocessor().Process(doc.CreateReader(), new Dictionary <string, string>());
            Compiler  compiler = new Compiler();
            Linker    linker   = new Linker();

            Intermediate intermediate = compiler.Compile(src);
            Output       output       = linker.Link(intermediate.Sections, OutputType.Product);

            RowIndexedList <Row> directoryRows = new RowIndexedList <Row>(output.Sections.SelectMany(sec => sec.Tables).Where(t => t.Name.Equals("Directory")).SelectMany(d => d.Rows));
            ComponentRow         componentRow  = output.Sections.SelectMany(sec => sec.Tables).Where(t => t.Name.Equals("Component")).SelectMany(c => c.Rows).Cast <ComponentRow>().Single();
            Row compFolder = directoryRows.Get(componentRow.Directory);

            Assert.NotNull(compFolder);
            Assert.Equal(AccessModifier.Private, compFolder.Access);
            Assert.Equal("BinFolder", compFolder[1]);
            Assert.Equal("comp", compFolder[2]);
        }
        public void Execute()
        {
            using (XmlTextWriter writer = new XmlTextWriter(this.OutputPath, Encoding.UTF8))
            {
                writer.WriteStartDocument();

                writer.WriteStartElement("BurnManifest", BurnCommon.BurnNamespace);

                // Write the condition, if there is one
                if (null != this.BundleInfo.Condition)
                {
                    writer.WriteElementString("Condition", this.BundleInfo.Condition);
                }

                // Write the log element if default logging wasn't disabled.
                if (!String.IsNullOrEmpty(this.BundleInfo.LogPrefix))
                {
                    writer.WriteStartElement("Log");
                    if (!String.IsNullOrEmpty(this.BundleInfo.LogPathVariable))
                    {
                        writer.WriteAttributeString("PathVariable", this.BundleInfo.LogPathVariable);
                    }
                    writer.WriteAttributeString("Prefix", this.BundleInfo.LogPrefix);
                    writer.WriteAttributeString("Extension", this.BundleInfo.LogExtension);
                    writer.WriteEndElement();
                }


                // Get update if specified.
                WixBundleUpdateRow updateRow = this.Output.Tables["WixBundleUpdate"].RowsAs <WixBundleUpdateRow>().FirstOrDefault();

                if (null != updateRow)
                {
                    writer.WriteStartElement("Update");
                    writer.WriteAttributeString("Location", updateRow.Location);
                    writer.WriteEndElement(); // </Update>
                }

                // Write the RelatedBundle elements

                // For the related bundles with duplicated identifiers the second instance is ignored (i.e. the Duplicates
                // enumeration in the index row list is not used).
                RowIndexedList <WixRelatedBundleRow> relatedBundles = new RowIndexedList <WixRelatedBundleRow>(this.Output.Tables["WixRelatedBundle"]);

                foreach (WixRelatedBundleRow relatedBundle in relatedBundles)
                {
                    writer.WriteStartElement("RelatedBundle");
                    writer.WriteAttributeString("Id", relatedBundle.Id);
                    writer.WriteAttributeString("Action", Convert.ToString(relatedBundle.Action, CultureInfo.InvariantCulture));
                    writer.WriteEndElement();
                }

                // Write the variables
                IEnumerable <WixBundleVariableRow> variables = this.Output.Tables["WixBundleVariable"].RowsAs <WixBundleVariableRow>();

                foreach (WixBundleVariableRow variable in variables)
                {
                    writer.WriteStartElement("Variable");
                    writer.WriteAttributeString("Id", variable.Id);
                    if (null != variable.Type)
                    {
                        writer.WriteAttributeString("Value", variable.Value);
                        writer.WriteAttributeString("Type", variable.Type);
                    }
                    writer.WriteAttributeString("Hidden", variable.Hidden ? "yes" : "no");
                    writer.WriteAttributeString("Persisted", variable.Persisted ? "yes" : "no");
                    writer.WriteEndElement();
                }

                // Write the searches
                foreach (WixSearchInfo searchinfo in this.OrderedSearches)
                {
                    searchinfo.WriteXml(writer);
                }

                // write the UX element
                writer.WriteStartElement("UX");
                if (!String.IsNullOrEmpty(this.BundleInfo.SplashScreenBitmapPath))
                {
                    writer.WriteAttributeString("SplashScreen", "yes");
                }

                // write the UX allPayloads...
                foreach (WixBundlePayloadRow payload in this.UXContainerPayloads)
                {
                    writer.WriteStartElement("Payload");
                    this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads);
                    writer.WriteEndElement();
                }

                writer.WriteEndElement(); // </UX>

                // write the catalog elements
                if (this.Catalogs.Any())
                {
                    foreach (WixBundleCatalogRow catalog in this.Catalogs)
                    {
                        writer.WriteStartElement("Catalog");
                        writer.WriteAttributeString("Id", catalog.Id);
                        writer.WriteAttributeString("Payload", catalog.Payload);
                        writer.WriteEndElement();
                    }
                }

                foreach (WixBundleContainerRow container in this.Containers.Values)
                {
                    if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id)
                    {
                        writer.WriteStartElement("Container");
                        this.WriteBurnManifestContainerAttributes(writer, this.ExecutableName, container);
                        writer.WriteEndElement();
                    }
                }

                foreach (WixBundlePayloadRow payload in this.Payloads.Values)
                {
                    if (PackagingType.Embedded == payload.Packaging && Compiler.BurnUXContainerId != payload.Container)
                    {
                        writer.WriteStartElement("Payload");
                        this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads);
                        writer.WriteEndElement();
                    }
                    else if (PackagingType.External == payload.Packaging)
                    {
                        writer.WriteStartElement("Payload");
                        this.WriteBurnManifestPayloadAttributes(writer, payload, false, this.Payloads);
                        writer.WriteEndElement();
                    }
                }

                foreach (WixBundleRollbackBoundaryRow rollbackBoundary in this.RollbackBoundaries)
                {
                    writer.WriteStartElement("RollbackBoundary");
                    writer.WriteAttributeString("Id", rollbackBoundary.ChainPackageId);
                    writer.WriteAttributeString("Vital", YesNoType.Yes == rollbackBoundary.Vital ? "yes" : "no");
                    writer.WriteAttributeString("Transaction", YesNoType.Yes == rollbackBoundary.Transaction ? "yes" : "no");
                    writer.WriteEndElement();
                }

                // Write the registration information...
                writer.WriteStartElement("Registration");

                writer.WriteAttributeString("Id", this.BundleInfo.BundleId.ToString("B"));
                writer.WriteAttributeString("ExecutableName", this.ExecutableName);
                writer.WriteAttributeString("PerMachine", this.BundleInfo.PerMachine ? "yes" : "no");
                writer.WriteAttributeString("Tag", this.BundleInfo.Tag);
                writer.WriteAttributeString("Version", this.BundleInfo.Version);
                writer.WriteAttributeString("ProviderKey", this.BundleInfo.ProviderKey);

                writer.WriteStartElement("Arp");
                writer.WriteAttributeString("Register", (0 < this.BundleInfo.DisableModify && this.BundleInfo.DisableRemove) ? "no" : "yes"); // do not register if disabled modify and remove.
                writer.WriteAttributeString("DisplayName", this.BundleInfo.Name);
                writer.WriteAttributeString("DisplayVersion", this.BundleInfo.Version);

                if (!String.IsNullOrEmpty(this.BundleInfo.Publisher))
                {
                    writer.WriteAttributeString("Publisher", this.BundleInfo.Publisher);
                }

                if (!String.IsNullOrEmpty(this.BundleInfo.HelpLink))
                {
                    writer.WriteAttributeString("HelpLink", this.BundleInfo.HelpLink);
                }

                if (!String.IsNullOrEmpty(this.BundleInfo.HelpTelephone))
                {
                    writer.WriteAttributeString("HelpTelephone", this.BundleInfo.HelpTelephone);
                }

                if (!String.IsNullOrEmpty(this.BundleInfo.AboutUrl))
                {
                    writer.WriteAttributeString("AboutUrl", this.BundleInfo.AboutUrl);
                }

                if (!String.IsNullOrEmpty(this.BundleInfo.UpdateUrl))
                {
                    writer.WriteAttributeString("UpdateUrl", this.BundleInfo.UpdateUrl);
                }

                if (!String.IsNullOrEmpty(this.BundleInfo.ParentName))
                {
                    writer.WriteAttributeString("ParentDisplayName", this.BundleInfo.ParentName);
                }

                if (1 == this.BundleInfo.DisableModify)
                {
                    writer.WriteAttributeString("DisableModify", "yes");
                }
                else if (2 == this.BundleInfo.DisableModify)
                {
                    writer.WriteAttributeString("DisableModify", "button");
                }

                if (this.BundleInfo.DisableRemove)
                {
                    writer.WriteAttributeString("DisableRemove", "yes");
                }
                writer.WriteEndElement(); // </Arp>

                // Get update registration if specified.
                WixUpdateRegistrationRow updateRegistrationInfo = this.Output.Tables["WixUpdateRegistration"].RowsAs <WixUpdateRegistrationRow>().FirstOrDefault();

                if (null != updateRegistrationInfo)
                {
                    writer.WriteStartElement("Update"); // <Update>
                    writer.WriteAttributeString("Manufacturer", updateRegistrationInfo.Manufacturer);

                    if (!String.IsNullOrEmpty(updateRegistrationInfo.Department))
                    {
                        writer.WriteAttributeString("Department", updateRegistrationInfo.Department);
                    }

                    if (!String.IsNullOrEmpty(updateRegistrationInfo.ProductFamily))
                    {
                        writer.WriteAttributeString("ProductFamily", updateRegistrationInfo.ProductFamily);
                    }

                    writer.WriteAttributeString("Name", updateRegistrationInfo.Name);
                    writer.WriteAttributeString("Classification", updateRegistrationInfo.Classification);
                    writer.WriteEndElement(); // </Update>
                }

                IEnumerable <Row> bundleTags = this.Output.Tables["WixBundleTag"].RowsAs <Row>();

                foreach (Row row in bundleTags)
                {
                    writer.WriteStartElement("SoftwareTag");
                    writer.WriteAttributeString("Filename", (string)row[0]);
                    writer.WriteAttributeString("Regid", (string)row[1]);
                    writer.WriteCData((string)row[4]);
                    writer.WriteEndElement();
                }

                writer.WriteEndElement(); // </Register>

                // write the Chain...
                writer.WriteStartElement("Chain");
                if (this.Chain.DisableRollback)
                {
                    writer.WriteAttributeString("DisableRollback", "yes");
                }

                if (this.Chain.DisableSystemRestore)
                {
                    writer.WriteAttributeString("DisableSystemRestore", "yes");
                }

                if (this.Chain.ParallelCache)
                {
                    writer.WriteAttributeString("ParallelCache", "yes");
                }

                // Index a few tables by package.
                ILookup <string, WixBundlePatchTargetCodeRow>    targetCodesByPatch       = this.Output.Tables["WixBundlePatchTargetCode"].RowsAs <WixBundlePatchTargetCodeRow>().ToLookup(r => r.MspPackageId);
                ILookup <string, WixBundleMsiFeatureRow>         msiFeaturesByPackage     = this.Output.Tables["WixBundleMsiFeature"].RowsAs <WixBundleMsiFeatureRow>().ToLookup(r => r.ChainPackageId);
                ILookup <string, WixBundleMsiPropertyRow>        msiPropertiesByPackage   = this.Output.Tables["WixBundleMsiProperty"].RowsAs <WixBundleMsiPropertyRow>().ToLookup(r => r.ChainPackageId);
                ILookup <string, WixBundlePayloadRow>            payloadsByPackage        = this.Payloads.Values.ToLookup(p => p.Package);
                ILookup <string, WixBundleRelatedPackageRow>     relatedPackagesByPackage = this.Output.Tables["WixBundleRelatedPackage"].RowsAs <WixBundleRelatedPackageRow>().ToLookup(r => r.ChainPackageId);
                ILookup <string, WixBundleSlipstreamMspRow>      slipstreamMspsByPackage  = this.Output.Tables["WixBundleSlipstreamMsp"].RowsAs <WixBundleSlipstreamMspRow>().ToLookup(r => r.ChainPackageId);
                ILookup <string, WixBundlePackageExitCodeRow>    exitCodesByPackage       = this.Output.Tables["WixBundlePackageExitCode"].RowsAs <WixBundlePackageExitCodeRow>().ToLookup(r => r.ChainPackageId);
                ILookup <string, WixBundlePackageCommandLineRow> commandLinesByPackage    = this.Output.Tables["WixBundlePackageCommandLine"].RowsAs <WixBundlePackageCommandLineRow>().ToLookup(r => r.ChainPackageId);

                // Build up the list of target codes from all the MSPs in the chain.
                List <WixBundlePatchTargetCodeRow> targetCodes = new List <WixBundlePatchTargetCodeRow>();

                foreach (PackageFacade package in this.OrderedPackages)
                {
                    writer.WriteStartElement(String.Format(CultureInfo.InvariantCulture, "{0}Package", package.Package.Type));

                    writer.WriteAttributeString("Id", package.Package.WixChainItemId);

                    switch (package.Package.Cache)
                    {
                    case YesNoAlwaysType.No:
                        writer.WriteAttributeString("Cache", "no");
                        break;

                    case YesNoAlwaysType.Yes:
                        writer.WriteAttributeString("Cache", "yes");
                        break;

                    case YesNoAlwaysType.Always:
                        writer.WriteAttributeString("Cache", "always");
                        break;
                    }

                    writer.WriteAttributeString("CacheId", package.Package.CacheId);
                    writer.WriteAttributeString("InstallSize", Convert.ToString(package.Package.InstallSize));
                    writer.WriteAttributeString("Size", Convert.ToString(package.Package.Size));
                    writer.WriteAttributeString("PerMachine", YesNoDefaultType.Yes == package.Package.PerMachine ? "yes" : "no");
                    writer.WriteAttributeString("Permanent", package.Package.Permanent ? "yes" : "no");
                    writer.WriteAttributeString("Vital", (YesNoType.Yes == package.Package.Vital) ? "yes" : "no");

                    if (null != package.Package.RollbackBoundary)
                    {
                        writer.WriteAttributeString("RollbackBoundaryForward", package.Package.RollbackBoundary);
                    }

                    if (!String.IsNullOrEmpty(package.Package.RollbackBoundaryBackward))
                    {
                        writer.WriteAttributeString("RollbackBoundaryBackward", package.Package.RollbackBoundaryBackward);
                    }

                    if (!String.IsNullOrEmpty(package.Package.LogPathVariable))
                    {
                        writer.WriteAttributeString("LogPathVariable", package.Package.LogPathVariable);
                    }

                    if (!String.IsNullOrEmpty(package.Package.RollbackLogPathVariable))
                    {
                        writer.WriteAttributeString("RollbackLogPathVariable", package.Package.RollbackLogPathVariable);
                    }

                    if (!String.IsNullOrEmpty(package.Package.InstallCondition))
                    {
                        writer.WriteAttributeString("InstallCondition", package.Package.InstallCondition);
                    }

                    if (WixBundlePackageType.Exe == package.Package.Type)
                    {
                        writer.WriteAttributeString("DetectCondition", package.ExePackage.DetectCondition);
                        writer.WriteAttributeString("InstallArguments", package.ExePackage.InstallCommand);
                        writer.WriteAttributeString("UninstallArguments", package.ExePackage.UninstallCommand);
                        writer.WriteAttributeString("RepairArguments", package.ExePackage.RepairCommand);
                        writer.WriteAttributeString("Repairable", package.ExePackage.Repairable ? "yes" : "no");
                        if (!String.IsNullOrEmpty(package.ExePackage.ExeProtocol))
                        {
                            writer.WriteAttributeString("Protocol", package.ExePackage.ExeProtocol);
                        }
                    }
                    else if (WixBundlePackageType.Msi == package.Package.Type)
                    {
                        writer.WriteAttributeString("ProductCode", package.MsiPackage.ProductCode);
                        writer.WriteAttributeString("Language", package.MsiPackage.ProductLanguage.ToString(CultureInfo.InvariantCulture));
                        writer.WriteAttributeString("Version", package.MsiPackage.ProductVersion);
                        writer.WriteAttributeString("DisplayInternalUI", package.MsiPackage.DisplayInternalUI ? "yes" : "no");
                        if (!String.IsNullOrEmpty(package.MsiPackage.UpgradeCode))
                        {
                            writer.WriteAttributeString("UpgradeCode", package.MsiPackage.UpgradeCode);
                        }
                    }
                    else if (WixBundlePackageType.Msp == package.Package.Type)
                    {
                        writer.WriteAttributeString("PatchCode", package.MspPackage.PatchCode);
                        writer.WriteAttributeString("PatchXml", package.MspPackage.PatchXml);
                        writer.WriteAttributeString("DisplayInternalUI", package.MspPackage.DisplayInternalUI ? "yes" : "no");

                        // If there is still a chance that all of our patches will target a narrow set of
                        // product codes, add the patch list to the overall list.
                        if (null != targetCodes)
                        {
                            if (!package.MspPackage.TargetUnspecified)
                            {
                                IEnumerable <WixBundlePatchTargetCodeRow> patchTargetCodes = targetCodesByPatch[package.MspPackage.ChainPackageId];

                                targetCodes.AddRange(patchTargetCodes);
                            }
                            else // we have a patch that targets the world, so throw the whole list away.
                            {
                                targetCodes = null;
                            }
                        }
                    }
                    else if (WixBundlePackageType.Msu == package.Package.Type)
                    {
                        writer.WriteAttributeString("DetectCondition", package.MsuPackage.DetectCondition);
                        writer.WriteAttributeString("KB", package.MsuPackage.MsuKB);
                    }

                    IEnumerable <WixBundleMsiFeatureRow> packageMsiFeatures = msiFeaturesByPackage[package.Package.WixChainItemId];

                    foreach (WixBundleMsiFeatureRow feature in packageMsiFeatures)
                    {
                        writer.WriteStartElement("MsiFeature");
                        writer.WriteAttributeString("Id", feature.Name);
                        writer.WriteEndElement();
                    }

                    IEnumerable <WixBundleMsiPropertyRow> packageMsiProperties = msiPropertiesByPackage[package.Package.WixChainItemId];

                    foreach (WixBundleMsiPropertyRow msiProperty in packageMsiProperties)
                    {
                        writer.WriteStartElement("MsiProperty");
                        writer.WriteAttributeString("Id", msiProperty.Name);
                        writer.WriteAttributeString("Value", msiProperty.Value);
                        if (!String.IsNullOrEmpty(msiProperty.Condition))
                        {
                            writer.WriteAttributeString("Condition", msiProperty.Condition);
                        }
                        writer.WriteEndElement();
                    }

                    IEnumerable <WixBundleSlipstreamMspRow> packageSlipstreamMsps = slipstreamMspsByPackage[package.Package.WixChainItemId];

                    foreach (WixBundleSlipstreamMspRow slipstreamMsp in packageSlipstreamMsps)
                    {
                        writer.WriteStartElement("SlipstreamMsp");
                        writer.WriteAttributeString("Id", slipstreamMsp.MspPackageId);
                        writer.WriteEndElement();
                    }

                    IEnumerable <WixBundlePackageExitCodeRow> packageExitCodes = exitCodesByPackage[package.Package.WixChainItemId];

                    foreach (WixBundlePackageExitCodeRow exitCode in packageExitCodes)
                    {
                        writer.WriteStartElement("ExitCode");

                        if (exitCode.Code.HasValue)
                        {
                            writer.WriteAttributeString("Code", unchecked ((uint)exitCode.Code).ToString(CultureInfo.InvariantCulture));
                        }
                        else
                        {
                            writer.WriteAttributeString("Code", "*");
                        }

                        writer.WriteAttributeString("Type", ((int)exitCode.Behavior).ToString(CultureInfo.InvariantCulture));
                        writer.WriteEndElement();
                    }

                    IEnumerable <WixBundlePackageCommandLineRow> packageCommandLines = commandLinesByPackage[package.Package.WixChainItemId];

                    foreach (WixBundlePackageCommandLineRow commandLine in packageCommandLines)
                    {
                        writer.WriteStartElement("CommandLine");
                        writer.WriteAttributeString("InstallArgument", commandLine.InstallArgument);
                        writer.WriteAttributeString("UninstallArgument", commandLine.UninstallArgument);
                        writer.WriteAttributeString("RepairArgument", commandLine.RepairArgument);
                        writer.WriteAttributeString("Condition", commandLine.Condition);
                        writer.WriteEndElement();
                    }

                    // Output the dependency information.
                    foreach (ProvidesDependency dependency in package.Provides)
                    {
                        // TODO: Add to wixpdb as an imported table, or link package wixpdbs to bundle wixpdbs.
                        dependency.WriteXml(writer);
                    }

                    IEnumerable <WixBundleRelatedPackageRow> packageRelatedPackages = relatedPackagesByPackage[package.Package.WixChainItemId];

                    foreach (WixBundleRelatedPackageRow related in packageRelatedPackages)
                    {
                        writer.WriteStartElement("RelatedPackage");
                        writer.WriteAttributeString("Id", related.Id);
                        if (!String.IsNullOrEmpty(related.MinVersion))
                        {
                            writer.WriteAttributeString("MinVersion", related.MinVersion);
                            writer.WriteAttributeString("MinInclusive", related.MinInclusive ? "yes" : "no");
                        }
                        if (!String.IsNullOrEmpty(related.MaxVersion))
                        {
                            writer.WriteAttributeString("MaxVersion", related.MaxVersion);
                            writer.WriteAttributeString("MaxInclusive", related.MaxInclusive ? "yes" : "no");
                        }
                        writer.WriteAttributeString("OnlyDetect", related.OnlyDetect ? "yes" : "no");

                        string[] relatedLanguages = related.Languages.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                        if (0 < relatedLanguages.Length)
                        {
                            writer.WriteAttributeString("LangInclusive", related.LangInclusive ? "yes" : "no");
                            foreach (string language in relatedLanguages)
                            {
                                writer.WriteStartElement("Language");
                                writer.WriteAttributeString("Id", language);
                                writer.WriteEndElement();
                            }
                        }
                        writer.WriteEndElement();
                    }

                    // Write any contained Payloads with the PackagePayload being first
                    writer.WriteStartElement("PayloadRef");
                    writer.WriteAttributeString("Id", package.Package.PackagePayload);
                    writer.WriteEndElement();

                    IEnumerable <WixBundlePayloadRow> packagePayloads = payloadsByPackage[package.Package.WixChainItemId];

                    foreach (WixBundlePayloadRow payload in packagePayloads)
                    {
                        if (payload.Id != package.Package.PackagePayload)
                        {
                            writer.WriteStartElement("PayloadRef");
                            writer.WriteAttributeString("Id", payload.Id);
                            writer.WriteEndElement();
                        }
                    }

                    writer.WriteEndElement(); // </XxxPackage>
                }
                writer.WriteEndElement();     // </Chain>

                if (null != targetCodes)
                {
                    foreach (WixBundlePatchTargetCodeRow targetCode in targetCodes)
                    {
                        writer.WriteStartElement("PatchTargetCode");
                        writer.WriteAttributeString("TargetCode", targetCode.TargetCode);
                        writer.WriteAttributeString("Product", targetCode.TargetsProductCode ? "yes" : "no");
                        writer.WriteEndElement();
                    }
                }

                // Write the ApprovedExeForElevation elements.
                IEnumerable <WixApprovedExeForElevationRow> approvedExesForElevation = this.Output.Tables["WixApprovedExeForElevation"].RowsAs <WixApprovedExeForElevationRow>();

                foreach (WixApprovedExeForElevationRow approvedExeForElevation in approvedExesForElevation)
                {
                    writer.WriteStartElement("ApprovedExeForElevation");
                    writer.WriteAttributeString("Id", approvedExeForElevation.Id);
                    writer.WriteAttributeString("Key", approvedExeForElevation.Key);

                    if (!String.IsNullOrEmpty(approvedExeForElevation.ValueName))
                    {
                        writer.WriteAttributeString("ValueName", approvedExeForElevation.ValueName);
                    }

                    if (approvedExeForElevation.Win64)
                    {
                        writer.WriteAttributeString("Win64", "yes");
                    }

                    writer.WriteEndElement();
                }

                writer.WriteEndDocument(); // </BurnManifest>
            }
        }
        public void Execute()
        {
            List <WixBundleMsiPackageRow> msiPackages = new List <WixBundleMsiPackageRow>();
            Dictionary <string, List <WixBundlePatchTargetCodeRow> > targetsProductCode = new Dictionary <string, List <WixBundlePatchTargetCodeRow> >();
            Dictionary <string, List <WixBundlePatchTargetCodeRow> > targetsUpgradeCode = new Dictionary <string, List <WixBundlePatchTargetCodeRow> >();

            foreach (PackageFacade facade in this.PackageFacades)
            {
                if (WixBundlePackageType.Msi == facade.Package.Type)
                {
                    // Keep track of all MSI packages.
                    msiPackages.Add(facade.MsiPackage);
                }
                else if (WixBundlePackageType.Msp == facade.Package.Type && facade.MspPackage.Slipstream)
                {
                    IEnumerable <WixBundlePatchTargetCodeRow> patchTargetCodeRows = this.WixBundlePatchTargetCodeTable.RowsAs <WixBundlePatchTargetCodeRow>().Where(r => r.MspPackageId == facade.Package.WixChainItemId);

                    // Index target ProductCodes and UpgradeCodes for slipstreamed MSPs.
                    foreach (WixBundlePatchTargetCodeRow row in patchTargetCodeRows)
                    {
                        if (row.TargetsProductCode)
                        {
                            List <WixBundlePatchTargetCodeRow> rows;
                            if (!targetsProductCode.TryGetValue(row.TargetCode, out rows))
                            {
                                rows = new List <WixBundlePatchTargetCodeRow>();
                                targetsProductCode.Add(row.TargetCode, rows);
                            }

                            rows.Add(row);
                        }
                        else if (row.TargetsUpgradeCode)
                        {
                            List <WixBundlePatchTargetCodeRow> rows;
                            if (!targetsUpgradeCode.TryGetValue(row.TargetCode, out rows))
                            {
                                rows = new List <WixBundlePatchTargetCodeRow>();
                                targetsUpgradeCode.Add(row.TargetCode, rows);
                            }
                        }
                    }
                }
            }

            RowIndexedList <Row> slipstreamMspRows = new RowIndexedList <Row>(SlipstreamMspTable);

            // Loop through the MSI and slipstream patches targeting it.
            foreach (WixBundleMsiPackageRow msi in msiPackages)
            {
                List <WixBundlePatchTargetCodeRow> rows;
                if (targetsProductCode.TryGetValue(msi.ProductCode, out rows))
                {
                    foreach (WixBundlePatchTargetCodeRow row in rows)
                    {
                        Debug.Assert(row.TargetsProductCode);
                        Debug.Assert(!row.TargetsUpgradeCode);

                        Row slipstreamMspRow = SlipstreamMspTable.CreateRow(row.SourceLineNumbers, false);
                        slipstreamMspRow[0] = msi.ChainPackageId;
                        slipstreamMspRow[1] = row.MspPackageId;

                        if (slipstreamMspRows.TryAdd(slipstreamMspRow))
                        {
                            SlipstreamMspTable.Rows.Add(slipstreamMspRow);
                        }
                    }

                    rows = null;
                }

                if (!String.IsNullOrEmpty(msi.UpgradeCode) && targetsUpgradeCode.TryGetValue(msi.UpgradeCode, out rows))
                {
                    foreach (WixBundlePatchTargetCodeRow row in rows)
                    {
                        Debug.Assert(!row.TargetsProductCode);
                        Debug.Assert(row.TargetsUpgradeCode);

                        Row slipstreamMspRow = SlipstreamMspTable.CreateRow(row.SourceLineNumbers, false);
                        slipstreamMspRow[0] = msi.ChainPackageId;
                        slipstreamMspRow[1] = row.MspPackageId;

                        if (slipstreamMspRows.TryAdd(slipstreamMspRow))
                        {
                            SlipstreamMspTable.Rows.Add(slipstreamMspRow);
                        }
                    }

                    rows = null;
                }
            }
        }