view Implab.ServiceHost/Unity/InjectionValueBuilder.cs @ 281:e0916ddc9950 v3 tip

code cleanup and refactoring
author cin
date Fri, 01 Jun 2018 21:35:24 +0300
parents 8714471e8d78
children
line wrap: on
line source

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml.Serialization;
using Unity.Injection;

namespace Implab.ServiceHost.Unity {

    public class InjectionParameterBuilder {

        readonly TypeResolver m_resolver;

        public Type DefaultType { get; private set; }

        public Type ValueType { get; private set; }

        object m_value;

        public object Value {
            get {
                if (!ValueSpecified)
                    throw new InvalidOperationException("The regular value must be set (dependency or array are not situable in this context)");
                return m_value;
            }
        }

        public bool ValueSpecified { get; private set; }

        InjectionParameterValue m_injection;

        public InjectionParameterValue Injection {
            get {
                if (m_injection == null)
                    throw new InvalidOperationException("The injection parameter is not specified");
                return m_injection;
            }
        }

        public bool InjectionSpecified {
            get { return m_injection != null; }
        }

        internal InjectionParameterBuilder(TypeResolver resolver, Type defaultType) {
            m_resolver = resolver;
            DefaultType = defaultType;
        }

        public Type ResolveInjectedValueType(string typeSpec) {
            if (string.IsNullOrEmpty(typeSpec)) {
                if (DefaultType == null)
                    throw new Exception("The type must be specified");
                return DefaultType;
            }
            return m_resolver.Resolve(typeSpec, true);
        }

        public Type ResolveType(string typeSpec) {
            return string.IsNullOrEmpty(typeSpec) ? null : m_resolver.Resolve(typeSpec, true);
        }

        public void SetValue(Type type, object value) {
            Safe.ArgumentNotNull(type, nameof(type));

            ValueType = type;
            m_value = value;
            ValueSpecified = true;

            m_injection = new InjectionParameter(type, value);
        }

        public void SetDependency(Type type, string name, bool optional) {
            Safe.ArgumentNotNull(type, nameof(type));

            ValueType = type;
            ValueSpecified = false;
            m_value = null;
            
            m_injection = optional ? (InjectionParameterValue)new OptionalParameter(type, name) : new ResolvedParameter(type, name);
        }

        internal void Visit(ArrayParameterElement arrayParameter) {
            Type itemsType = null;
            var arrayType = string.IsNullOrEmpty(arrayParameter.TypeName) ? null : ResolveType(arrayParameter.TypeName);

            if (arrayType == null)
                arrayType = DefaultType;


            if (!string.IsNullOrEmpty(arrayParameter.ItemsType)) {
                itemsType = ResolveType(arrayParameter.ItemsType);
                arrayType = itemsType.MakeArrayType();
            } else {
                itemsType = GetItemsType(arrayType);
            }

            if (itemsType == null)
                throw new Exception("Failed to determine array elements type");

            InjectionParameterValue[] injections = (arrayParameter.Items ?? new AbstractInjectionParameter[0])
                .Select(x => {
                    var builder = new InjectionParameterBuilder(m_resolver, itemsType);
                    x.Visit(builder);
                    return builder.Injection;
                })
                .ToArray();

            var array = itemsType.IsGenericParameter ?
                (InjectionParameterValue)new GenericResolvedArrayParameter(itemsType.Name, injections) :
                new ResolvedArrayParameter(itemsType, injections);

            ValueType = arrayType;
            m_value = null;
            ValueSpecified = false;
            
            m_injection = array;
        }

        Type GetItemsType(Type collectionType) {
            if (collectionType == null)
                return null;

            Type itemsType = null;

            if (collectionType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) {
                itemsType = collectionType.GetGenericArguments()[0];
            } else if (collectionType == typeof(IEnumerable)) {
                itemsType = typeof(object);
            } else {
                itemsType = collectionType.GetInterface(typeof(IEnumerable<>).FullName)?.GetGenericArguments()[0];
            }
            
            return itemsType;
        }
    }
}