File: Shared\Options\ListValueOption.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//--------------------------------------------------------------------------------------------
// C`opyright(c) 2015 Microsoft Corporation
//--------------------------------------------------------------------------------------------
 
using Newtonsoft.Json.Linq;
 
using System.Collections;
using System.Collections.Generic;
using System.Linq;
 
namespace Microsoft.Tools.ServiceModel.Svcutil
{
    /// <summary>
    /// Represents an option that can be specified multiple times in the command line.
    /// Setting a value in the command line for an option that can be set multiple times is like adding an element to the collection.
    /// </summary>
    internal class ListValueOption<TValue> : OptionBase
    {
        private ListValue<TValue> InnerList { get; }
 
        public ListValueOption(string name) : base(name, new ListValue<TValue>())
        {
            this.InnerList = this.Value as ListValue<TValue>;
            this.InnerList.Owner = this;
 
            // because setting the value of a collection option means Add value, we need to hook up events 
            // to be able to notify the base class about the value changing/changed events, let's hook up handlers.
            this.InnerList.Inserting += this.InnerList_Inserting;
            this.InnerList.Inserted += this.InnerList_Inserted;
            this.InnerList.Removing += this.InnerList_Removing;
            this.InnerList.Removed += this.InnerList_Removed;
        }
 
        public override bool HasValue { get { return this.InnerList.Count > 0; } }
 
        public override bool CanSerialize
        {
            get { return base.CanSerialize && this.HasValue; }
            set { base.CanSerialize = value; }
        }
 
        private void InnerList_Inserting(object sender, ListOptionEventArgs e)
        {
            e.Value = OnValueChanging(e.Value);
        }
 
        private void InnerList_Inserted(object sender, ListOptionEventArgs e)
        {
            OnValueChanged(e.Value);
        }
 
        private void InnerList_Removing(object sender, ListOptionEventArgs e)
        {
            OnValueChanging(e.Value);
        }
 
        private void InnerList_Removed(object sender, ListOptionEventArgs e)
        {
            OnValueChanged(e.Value);
        }
 
        /// <summary>
        /// Core method for setting the option and/or adding values to the collection.
        /// Setting the value has the same semantics as when the option is specified in the command line, when specified multiple times 
        /// it is equivalent to having a collection of values not a collection of options and the operation is Add instead of SetValue.
        /// </summary>
        protected override void SetValue(object value)
        {
            if (value == null)
            {
                this.InnerList.Clear();
            }
            else
            {
                if (value is IList<TValue> list)
                {
                    this.InnerList.AddRange(list);
                }
                else
                {
                    this.InnerList.Add(OptionValueParser.ParseValue<TValue>(value, this));
                }
            }
        }
 
        protected override object OnValueChanging(object value)
        {
            // allow base class to notify event handlers (if any) to pre-process the value.
            value = base.OnValueChanging(value);
 
            // a null value means, the value is being removed.
            if (value != null)
            {
                // ensure the value is of the right type.
                value = OptionValueParser.ParseValue<TValue>(value, this);
            }
            return value;
        }
 
        protected override object OnDeserializing(JToken jToken)
        {
            // allow base class to notify event handlers (if any) to pre-process the JSON value.
            var value = base.OnDeserializing(jToken);
            var collection = value as ICollection;
 
            if (value == null)
            {
                if (jToken.Type == JTokenType.Array)
                {
                    OptionValueParser.ThrowInvalidValueIf(jToken.Any(i => i.Type != JTokenType.String), jToken, this);
                    collection = jToken.Select(i => i.Value<string>()).ToList();
                }
                else if (jToken.Type == JTokenType.String)
                {
                    // allow for a single-value list to be represented with a string.
                    collection = new List<string>() { jToken.Value<string>() };
                }
            }
 
            OptionValueParser.ThrowInvalidValueIf(collection == null, jToken, this);
 
            foreach (var item in collection)
            {
                this.InnerList.Add(OptionValueParser.ParseValue<TValue>(item, this));
            }
 
            // ensure the underlying value is the inner list.
            return this.InnerList;
        }
    }
}