File: System\Net\Http\Headers\ProductHeaderValue.cs
Web Access
Project: src\src\libraries\System.Net.Http\src\System.Net.Http.csproj (System.Net.Http)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
 
namespace System.Net.Http.Headers
{
    public class ProductHeaderValue : ICloneable
    {
        private readonly string _name;
        private readonly string? _version;
 
        public string Name
        {
            get { return _name; }
        }
 
        // We can't use the System.Version type, since a version can be e.g. "x11".
        public string? Version
        {
            get { return _version; }
        }
 
        public ProductHeaderValue(string name)
            : this(name, null)
        {
        }
 
        public ProductHeaderValue(string name, string? version)
        {
            HeaderUtilities.CheckValidToken(name);
 
            if (!string.IsNullOrEmpty(version))
            {
                HeaderUtilities.CheckValidToken(version);
                _version = version;
            }
 
            _name = name;
        }
 
        private ProductHeaderValue(ProductHeaderValue source)
        {
            Debug.Assert(source != null);
 
            _name = source._name;
            _version = source._version;
        }
 
        public override string ToString()
        {
            if (string.IsNullOrEmpty(_version))
            {
                return _name;
            }
            return _name + "/" + _version;
        }
 
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            ProductHeaderValue? other = obj as ProductHeaderValue;
 
            if (other == null)
            {
                return false;
            }
 
            return string.Equals(_name, other._name, StringComparison.OrdinalIgnoreCase) &&
                string.Equals(_version, other._version, StringComparison.OrdinalIgnoreCase);
        }
 
        public override int GetHashCode()
        {
            int result = StringComparer.OrdinalIgnoreCase.GetHashCode(_name);
 
            if (!string.IsNullOrEmpty(_version))
            {
                result ^= StringComparer.OrdinalIgnoreCase.GetHashCode(_version);
            }
 
            return result;
        }
 
        public static ProductHeaderValue Parse(string input)
        {
            int index = 0;
            return (ProductHeaderValue)GenericHeaderParser.SingleValueProductParser.ParseValue(input, null, ref index);
        }
 
        public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out ProductHeaderValue? parsedValue)
        {
            int index = 0;
            parsedValue = null;
 
            if (GenericHeaderParser.SingleValueProductParser.TryParseValue(input, null, ref index, out object? output))
            {
                parsedValue = (ProductHeaderValue)output!;
                return true;
            }
            return false;
        }
 
        internal static int GetProductLength(string input, int startIndex, out ProductHeaderValue? parsedValue)
        {
            Debug.Assert(startIndex >= 0);
 
            parsedValue = null;
 
            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
            {
                return 0;
            }
 
            // Parse the name string: <name> in '<name>/<version>'.
            int nameLength = HttpRuleParser.GetTokenLength(input, startIndex);
 
            if (nameLength == 0)
            {
                return 0;
            }
 
            string name = input.Substring(startIndex, nameLength);
            int current = startIndex + nameLength;
            current += HttpRuleParser.GetWhitespaceLength(input, current);
 
            if ((current == input.Length) || (input[current] != '/'))
            {
                parsedValue = new ProductHeaderValue(name);
                return current - startIndex;
            }
 
            current++; // Skip '/' delimiter.
            current += HttpRuleParser.GetWhitespaceLength(input, current);
 
            // Parse the name string: <version> in '<name>/<version>'.
            int versionLength = HttpRuleParser.GetTokenLength(input, current);
 
            if (versionLength == 0)
            {
                return 0; // If there is a '/' separator it must be followed by a valid token.
            }
 
            string version = input.Substring(current, versionLength);
 
            current += versionLength;
            current += HttpRuleParser.GetWhitespaceLength(input, current);
 
            parsedValue = new ProductHeaderValue(name, version);
            return current - startIndex;
        }
 
        object ICloneable.Clone()
        {
            return new ProductHeaderValue(this);
        }
    }
}