File: PlatformManifest.cs
Web Access
Project: ..\..\..\src\Utilities\Microsoft.Build.Utilities.csproj (Microsoft.Build.Utilities.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Microsoft.Build.Shared;
using Microsoft.Build.Shared.FileSystem;
 
#nullable disable
 
namespace Microsoft.Build.Utilities
{
    /// <summary>
    /// Structure to represent the information contained in Platform.xml
    /// </summary>
    internal class PlatformManifest
    {
        /// <summary>
        /// Location of Platform.xml
        /// </summary>
        private readonly string _pathToManifest;
 
        /// <summary>
        /// Constructor
        /// Takes the location of Platform.xml and populates the structure with manifest data
        /// </summary>
        public PlatformManifest(string pathToManifest)
        {
            ErrorUtilities.VerifyThrowArgumentLength(pathToManifest);
            _pathToManifest = pathToManifest;
            LoadManifestFile();
        }
 
        /// <summary>
        /// Platform name
        /// </summary>
        public string Name { get; private set; }
 
        /// <summary>
        /// Platform friendly name
        /// </summary>
        public string FriendlyName { get; private set; }
 
        /// <summary>
        /// Platform version
        /// </summary>
        public string PlatformVersion { get; private set; }
 
        /// <summary>
        /// The platforms that this platform depends on.
        /// Item1: Platform name
        /// Item2: Platform version
        /// </summary>
        public ICollection<DependentPlatform> DependentPlatforms { get; private set; }
 
        /// <summary>
        /// The contracts contained by this platform
        /// Item1: Contract name
        /// Item2: Contract version
        /// </summary>
        public ICollection<ApiContract> ApiContracts { get; private set; }
 
        public bool VersionedContent { get; private set; }
 
        /// <summary>
        /// Flag set to true if an exception occurred while reading the manifest
        /// </summary>
        public bool ReadError => !string.IsNullOrEmpty(ReadErrorMessage);
 
        /// <summary>
        /// Message from exception thrown while reading manifest
        /// </summary>
        public string ReadErrorMessage { get; private set; }
 
        /// <summary>
        /// Load content of Platform.xml
        /// </summary>
        private void LoadManifestFile()
        {
            /*
               Platform.xml format:
 
               <ApplicationPlatform name="UAP" friendlyName="Universal Application Platform" version="1.0.0.0">
                  <DependentPlatform name="UAP" version="1.0.0.0" />
                  <ContainedApiContracts>
                     <ApiContract name="UAP" version="1.0.0.0" />
                  </ContainedApiContracts>
               </ApplicationPlatform>
             */
            try
            {
                string platformManifestPath = Path.Combine(_pathToManifest, "Platform.xml");
 
                if (FileSystems.Default.FileExists(platformManifestPath))
                {
                    XmlDocument doc = new XmlDocument();
                    XmlReaderSettings readerSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, CloseInput = true };
 
                    FileStream fs = File.OpenRead(platformManifestPath);
                    using (XmlReader xmlReader = XmlReader.Create(fs, readerSettings))
                    {
                        doc.Load(xmlReader);
                    }
 
                    XmlElement rootElement = null;
 
                    foreach (XmlNode childNode in doc.ChildNodes)
                    {
                        if (childNode.NodeType == XmlNodeType.Element &&
                            string.Equals(childNode.Name, Elements.ApplicationPlatform, StringComparison.Ordinal))
                        {
                            rootElement = (XmlElement)childNode;
                            break;
                        }
                    }
 
                    DependentPlatforms = new List<DependentPlatform>();
                    ApiContracts = new List<ApiContract>();
 
                    if (rootElement != null)
                    {
                        Name = rootElement.GetAttribute(Attributes.Name);
                        FriendlyName = rootElement.GetAttribute(Attributes.FriendlyName);
                        PlatformVersion = rootElement.GetAttribute(Attributes.Version);
 
                        foreach (XmlNode childNode in rootElement.ChildNodes)
                        {
                            if (!(childNode is XmlElement childElement))
                            {
                                continue;
                            }
 
                            if (ApiContract.IsContainedApiContractsElement(childElement.Name))
                            {
                                ApiContract.ReadContractsElement(childElement, ApiContracts);
                            }
                            else if (ApiContract.IsVersionedContentElement(childElement.Name))
                            {
                                bool.TryParse(childElement.InnerText, out bool versionedContent);
                                VersionedContent = versionedContent;
                            }
                            else if (string.Equals(childElement.Name, Elements.DependentPlatform, StringComparison.Ordinal))
                            {
                                DependentPlatforms.Add(new DependentPlatform(childElement.GetAttribute(Attributes.Name), childElement.GetAttribute(Attributes.Version)));
                            }
                        }
                    }
                }
                else
                {
                    ReadErrorMessage = ResourceUtilities.FormatResourceStringStripCodeAndKeyword("PlatformManifest.MissingPlatformXml", platformManifestPath);
                }
            }
            catch (Exception e) when (!ExceptionHandling.IsCriticalException(e))
            {
                ReadErrorMessage = e.Message;
            }
        }
 
        /// <summary>
        /// Represents a dependency on another platform
        /// </summary>
        internal struct DependentPlatform
        {
            /// <summary>
            /// Name of the platform on which this platform depends
            /// </summary>
            internal readonly string Name;
 
            /// <summary>
            /// Version of the platform on which this platform depends
            /// </summary>
            internal readonly string Version;
 
            /// <summary>
            /// Constructor
            /// </summary>
            internal DependentPlatform(string name, string version)
            {
                Name = name;
                Version = version;
            }
        }
 
        /// <summary>
        /// Helper class with element names in Platform.xml
        /// </summary>
        private static class Elements
        {
            /// <summary>
            /// Root element
            /// </summary>
            public const string ApplicationPlatform = "ApplicationPlatform";
 
            /// <summary>
            /// Element describing a platform this platform is dependent on
            /// </summary>
            public const string DependentPlatform = "DependentPlatform";
        }
 
        /// <summary>
        /// Helper class with attribute names in Platform.xml
        /// </summary>
        private static class Attributes
        {
            /// <summary>
            /// Name associated with this element
            /// </summary>
            public const string Name = "name";
 
            /// <summary>
            /// Friendly name associated with this element
            /// </summary>
            public const string FriendlyName = "friendlyName";
 
            /// <summary>
            /// Version associated with this element
            /// </summary>
            public const string Version = "version";
        }
    }
}