File: System\Net\Http\Headers\NameValueWithParametersHeaderValue.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.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
 
namespace System.Net.Http.Headers
{
    // According to the RFC, in places where a "parameter" is required, the value is mandatory
    // (e.g. Media-Type, Accept). However, we don't introduce a dedicated type for this.
    public class NameValueWithParametersHeaderValue : NameValueHeaderValue, ICloneable
    {
        private static readonly Func<NameValueHeaderValue> s_nameValueCreator = CreateNameValue;
 
        private UnvalidatedObjectCollection<NameValueHeaderValue>? _parameters;
 
        public ICollection<NameValueHeaderValue> Parameters =>
            _parameters ??= new UnvalidatedObjectCollection<NameValueHeaderValue>();
 
        public NameValueWithParametersHeaderValue(string name)
            : base(name)
        {
        }
 
        public NameValueWithParametersHeaderValue(string name, string? value)
            : base(name, value)
        {
        }
 
        internal NameValueWithParametersHeaderValue()
        {
        }
 
        protected NameValueWithParametersHeaderValue(NameValueWithParametersHeaderValue source)
            : base(source)
        {
            _parameters = source._parameters.Clone();
        }
 
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            bool result = base.Equals(obj);
 
            if (result)
            {
                NameValueWithParametersHeaderValue? other = obj as NameValueWithParametersHeaderValue;
 
                if (other == null)
                {
                    return false;
                }
                return HeaderUtilities.AreEqualCollections(_parameters, other._parameters);
            }
 
            return false;
        }
 
        public override int GetHashCode()
        {
            return base.GetHashCode() ^ NameValueHeaderValue.GetHashCode(_parameters);
        }
 
        public override string ToString()
        {
            string baseString = base.ToString();
            StringBuilder sb = StringBuilderCache.Acquire();
            sb.Append(baseString);
            NameValueHeaderValue.ToString(_parameters, ';', true, sb);
            return StringBuilderCache.GetStringAndRelease(sb);
        }
 
        public static new NameValueWithParametersHeaderValue Parse(string input)
        {
            int index = 0;
            return (NameValueWithParametersHeaderValue)GenericHeaderParser.SingleValueNameValueWithParametersParser
                .ParseValue(input, null, ref index);
        }
 
        public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out NameValueWithParametersHeaderValue? parsedValue)
        {
            int index = 0;
            parsedValue = null;
 
            if (GenericHeaderParser.SingleValueNameValueWithParametersParser.TryParseValue(input,
                null, ref index, out object? output))
            {
                parsedValue = (NameValueWithParametersHeaderValue)output!;
                return true;
            }
            return false;
        }
 
        internal static int GetNameValueWithParametersLength(string? input, int startIndex, out object? parsedValue)
        {
            Debug.Assert(input != null);
            Debug.Assert(startIndex >= 0);
 
            parsedValue = null;
 
            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
            {
                return 0;
            }
 
            int nameValueLength = NameValueHeaderValue.GetNameValueLength(input, startIndex,
                s_nameValueCreator, out NameValueHeaderValue? nameValue);
 
            if (nameValueLength == 0)
            {
                return 0;
            }
 
            int current = startIndex + nameValueLength;
            current += HttpRuleParser.GetWhitespaceLength(input, current);
            NameValueWithParametersHeaderValue? nameValueWithParameters =
                nameValue as NameValueWithParametersHeaderValue;
            Debug.Assert(nameValueWithParameters != null);
 
            // So far we have a valid name/value pair. Check if we have also parameters for the name/value pair. If
            // yes, parse parameters. E.g. something like "name=value; param1=value1; param2=value2".
            if ((current < input.Length) && (input[current] == ';'))
            {
                current++; // skip delimiter.
                int parameterLength = NameValueHeaderValue.GetNameValueListLength(input, current, ';',
                    (UnvalidatedObjectCollection<NameValueHeaderValue>)nameValueWithParameters.Parameters);
 
                if (parameterLength == 0)
                {
                    return 0;
                }
 
                parsedValue = nameValueWithParameters;
                return current + parameterLength - startIndex;
            }
 
            // We have a name/value pair without parameters.
            parsedValue = nameValueWithParameters;
            return current - startIndex;
        }
 
        private static NameValueHeaderValue CreateNameValue()
        {
            return new NameValueWithParametersHeaderValue();
        }
 
        object ICloneable.Clone()
        {
            return new NameValueWithParametersHeaderValue(this);
        }
    }
}