/// <summary>
 /// Saves the manifest stream to a file.
 /// </summary>
 /// <param name="manifest">Manifest to save.</param>
 /// <param name="path">Path to save manifest to.</param>
 private void SaveManifest(VsixManifest manifest, string path)
 {
     using (Stream file = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Delete))
     {
         using (Stream manifestStream = manifest.GetStream())
         {
             int    read   = 0;
             byte[] buffer = new byte[65536];
             while (0 < (read = manifestStream.Read(buffer, 0, buffer.Length)))
             {
                 file.Write(buffer, 0, read);
             }
         }
     }
 }
        /// <summary>
        /// Generates the final output from a set of intermediates.
        /// </summary>
        /// <param name="intermediates">Intermediates that provide data to be generated.</param>
        /// <param name="outputPath">Path for output file to be generated.</param>
        public override void Generate(IEnumerable <Intermediate> intermediates, string outputPath)
        {
            VsixManifest manifest = new VsixManifest(this);

            manifest.ProcessIntermediates(intermediates);
            if (this.EncounteredError)
            {
                return;
            }

            manifest.Validate(this.ValidationErrorHandler);
            if (this.EncounteredError)
            {
                string tempPath = Path.GetTempFileName();
                this.OnMessage(new CompilerMessageEventArgs(CompilerMessage.SavedManifest(tempPath), null, 0, 0));

                this.SaveManifest(manifest, tempPath);
                return;
            }

            FileTransfer packageTransfer = FileTransfer.Create(null, Path.GetTempFileName(), outputPath, "VsixPackage", true);

            using (Package package = Package.Open(packageTransfer.Source, FileMode.Create))
            {
                // Add all the manifest files.
                foreach (PackageFile file in manifest.Files)
                {
                    this.OnMessage(new CompilerMessageEventArgs(CompilerMessage.CompressFile(file.SourcePath), file.File.LineNumber));

                    using (Stream fileStream = File.Open(file.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        PackagePart part = package.CreatePart(new Uri(file.PartUri, UriKind.Relative), file.MimeType, CompressionOption.Normal);
                        this.SaveStreamToPart(fileStream, part);
                    }
                }

                // Save the manifest to the package.
                using (Stream manifestStream = manifest.GetStream())
                {
                    PackagePart part = package.CreatePart(new Uri("/extension.vsixmanifest", UriKind.Relative), "text/xml", CompressionOption.Normal);
                    this.SaveStreamToPart(manifestStream, part);
                }
            }

            FileTransfer.ExecuteTransfer(this, packageTransfer);
        }
        /// <summary>
        /// Creates the manifest document from the provided intermediates. Discards a previous
        /// manifest document if present.
        /// </summary>
        /// <param name="intermediates">Intermediates to process into the AppX manifest.</param>
        public void ProcessIntermediates(IEnumerable <Intermediate> intermediates)
        {
            this.document = null;
            this.Files    = new List <PackageFile>();

            Package            package           = null;
            Version            minNetfx          = null;
            Version            maxNetfx          = null;
            SortedSet <string> supportedProducts = new SortedSet <string>(StringComparer.OrdinalIgnoreCase);
            XElement           xContent          = new XElement(VsixNamespace + "Content");
            XElement           xReferences       = new XElement(VsixNamespace + "References");

            foreach (Intermediate intermediate in intermediates)
            {
                foreach (PackageItem item in intermediate.Items)
                {
                    if (item.System)
                    {
                        continue;
                    }

                    // Files are processed differently since we need to go search for them on disk and such.
                    if (item is File)
                    {
                        PackageFile packageFile;
                        if (PackageFile.TryCreate(backend, (File)item, out packageFile))
                        {
                            this.Files.Add(packageFile);
                        }
                    }
                    else if (item is Package)
                    {
                        if (package == null)
                        {
                            package = (Package)item;
                        }
                        else
                        {
                            this.backend.OnMessage(new CompilerMessageEventArgs(CompilerMessage.HighlanderElement("Package"), item));
                        }
                    }
                    else if (item is Prerequisite)
                    {
                        Prerequisite prereq = (Prerequisite)item;
                        switch (prereq.On.ToLowerInvariant())
                        {
                        case "netfx":
                            if (null == minNetfx)
                            {
                                if (prereq.Version == null)
                                {
                                    this.backend.OnMessage(new CompilerMessageEventArgs(CompilerMessage.RequiredAttribute("Prerequisite", "Version"), prereq));
                                }
                                else
                                {
                                    minNetfx = prereq.Version;
                                }

                                if (prereq.MaxVersion != null)
                                {
                                    maxNetfx = prereq.MaxVersion;
                                }
                            }
                            else
                            {
                                this.backend.OnMessage(new CompilerMessageEventArgs(CompilerMessage.HighlanderElementWithAttributeValue("Prerequisite", "On", prereq.On), prereq));
                            }
                            break;

                        case "vs":
                            if (prereq.Version == null)
                            {
                                this.backend.OnMessage(new CompilerMessageEventArgs(CompilerMessage.RequiredAttribute("Prerequisite", "Version"), prereq));
                            }
                            else
                            {
                                string edition = Product.GetEdition(prereq);
                                if (String.IsNullOrEmpty(edition))
                                {
                                    this.backend.OnMessage(new CompilerMessageEventArgs(CompilerMessage.RequiredAttribute("Vsix", "Product.Edition"), prereq));
                                }
                                else
                                {
                                    // Ensure that if the edition matches one of the well known values that it has the appropriate casing.
                                    string[] editionMatches = new string[] { "IntegratedShell", "Pro", "Premium", "Ultimate", "VWDExpress", "VCSExpress", "VBExpress", "VCExpress", "Express_All" };
                                    foreach (string editionMatch in editionMatches)
                                    {
                                        if (edition.Equals(editionMatch, StringComparison.OrdinalIgnoreCase))
                                        {
                                            edition = editionMatch;
                                            break;
                                        }
                                    }

                                    supportedProducts.Add(String.Concat(prereq.Version.ToString(), "\\", edition));
                                }
                            }
                            break;

                        default:
                            this.backend.OnMessage(new CompilerMessageEventArgs(CompilerMessage.InvalidAttributeValue("Prerequisite", "On", prereq.On), prereq));
                            break;
                        }
                    }
                    else if (item is Dependency)
                    {
                        Dependency dependency = (Dependency)item;
                        XElement   reference  = new XElement(VsixNamespace + "Reference",
                                                             new XAttribute("Id", dependency.Name),
                                                             new XElement(VsixNamespace + "Name", dependency.Publisher)
                                                             );

                        if (dependency.Version != null)
                        {
                            reference.Add(new XAttribute("MinVersion", dependency.Version.ToString()));
                        }

                        if (dependency.MaxVersion != null)
                        {
                            reference.Add(new XAttribute("MaxVersion", dependency.MaxVersion.ToString()));
                        }

                        xReferences.Add(reference);
                    }
                    else if (item is Vspackage)
                    {
                        Vspackage vspackage = (Vspackage)item;

                        // TODO: verify file specified
                        string pkgdef = VsixManifest.StripRootFolderReference(vspackage.File.Path);

                        // TODO: warn if file extension not .pkgdef.
                        xContent.Add(new XElement(VsixNamespace + "VsPackage", pkgdef));
                    }
                }
            }

            if (package != null)
            {
                // TODO: verify DisplayName <= 50 chars
                // TODO: verify Description <= 1000 chars
                // TODO: verify Manufacturer
                // TODO: verify Version
                // TODO: verify Languages[0] only one.

                XElement xIdentifier = new XElement(VsixNamespace + "Identifier",
                                                    new XAttribute("Id", package.Name),
                                                    new XElement(VsixNamespace + "Name", package.DisplayName),
                                                    new XElement(VsixNamespace + "Author", package.Manufacturer),
                                                    new XElement(VsixNamespace + "Version", package.Version.ToString()),
                                                    new XElement(VsixNamespace + "Description", package.Description),
                                                    new XElement(VsixNamespace + "Locale", this.backend.Languages[0].LCID)
                                                    );

                string image = (package.Image == null) ? null : package.Image.NonqualifiedName;
                if (!string.IsNullOrEmpty(image))
                {
                    xIdentifier.Add(VsixNamespace + "Icon", image);
                }

                xIdentifier.Add(new XElement(VsixNamespace + "InstalledByMsi", "false"));

                XElement xSupportedProducts = new XElement(VsixNamespace + "SupportedProducts");
                xIdentifier.Add(xSupportedProducts);
                string   previonsVersion = null;
                XElement xVisualStudio   = null;
                foreach (string supported in supportedProducts)
                {
                    string[] versionEdition = supported.Split(new char[] { '\\' }, 2);
                    if (!versionEdition[0].Equals(previonsVersion))
                    {
                        xVisualStudio = new XElement(VsixNamespace + "VisualStudio",
                                                     new XAttribute("Version", versionEdition[0]));
                        xSupportedProducts.Add(xVisualStudio);

                        previonsVersion = versionEdition[0];
                    }

                    xVisualStudio.Add(new XElement(VsixNamespace + "Edition", versionEdition[1]));
                }

                if (null != minNetfx)
                {
                    XElement xSupportedFrameworkRuntime = new XElement(VsixNamespace + "SupportedFrameworkRuntimeEdition",
                                                                       new XAttribute("MinVersion", minNetfx.ToString())
                                                                       );

                    if (null != maxNetfx)
                    {
                        xSupportedFrameworkRuntime.Add(new XAttribute("MaxVersion", maxNetfx.ToString()));
                    }

                    xIdentifier.Add(xSupportedFrameworkRuntime);
                }

                if (package.Framework)
                {
                    xIdentifier.Add(VsixNamespace + "SystemComponent", "true");
                }

                // Now put the manifest together.
                XElement xRoot = new XElement(VsixNamespace + "Vsix",
                                              new XAttribute("Version", "1.0"),
                                              xIdentifier
                                              );

                if (xReferences.HasElements)
                {
                    xRoot.Add(xReferences);
                }

                if (xContent.HasElements)
                {
                    xRoot.Add(xContent);
                }

                this.document = new XDocument(xRoot);
            }
        }