view Implab.ServiceHost/Unity/TypeResolver.cs @ 278:6691aff01de1 v3

Implab: added XmlDefaultSeializer (SerializersPool is now obsolete) Implab.ServiceHost: rewritten TypeReference (added support for nested types), stable API
author cin
date Thu, 03 May 2018 09:59:44 +0300
parents 963b17c275be
children 8714471e8d78
line wrap: on
line source

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Implab.Diagnostics;

namespace Implab.ServiceHost.Unity {
    using System.Diagnostics;
    using static Trace<TypeResolver>;
    public class TypeResolver {
        readonly Dictionary<string, Type> m_cache = new Dictionary<string, Type>();

        Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled);
        readonly LinkedList<string> m_namespases = new LinkedList<string>();

        internal Type Resolve(string ns, string typeName) {
            var fullName = string.IsNullOrEmpty(ns) ? typeName : $"{ns}.{typeName}";

            return ProbeInNamespaces(fullName);
        }

        public Type Resolve(TypeReference typeReference, bool throwOnFail) {
            var context = new TypeResolutionContext(this, throwOnFail);
            typeReference.Visit(context);
            return context.MakeType();
        }

        public Type Resolve(string typeSpec, bool throwOnFail) {
            var typeReference = TypeReference.Parse(typeSpec);
            return Resolve(typeReference, throwOnFail);
        }

        LinkedListNode<string> m_insertAt;

        readonly TypeResolver m_parent;

        public TypeResolver() : this(null) {
        }

        public TypeResolver(TypeResolver parent) {
            m_parent = parent;
            m_insertAt = new LinkedListNode<string>(string.Empty);
            m_namespases.AddFirst(m_insertAt);
        }

        public void AddNamespace(string ns) {
            Safe.ArgumentMatch(ns, nameof(ns), _nsRx);
            if (m_insertAt != null)
                m_namespases.AddAfter(m_insertAt, ns);
            else
                m_namespases.AddFirst(ns);
        }

        public void AddMapping(string typeName, Type type) {
            Safe.ArgumentNotEmpty(typeName, nameof(typeName));
            Safe.ArgumentNotNull(type, nameof(type));

            m_cache[typeName] = type;
        }

        Type ProbeInNamespaces(string localName) {

            Type resolved;
            if (!m_cache.TryGetValue(localName, out resolved)) {
                foreach (var ns in m_namespases) {
                    var typeName = string.IsNullOrEmpty(ns) ? localName : $"{ns}.{localName}";
                    resolved = Probe(typeName);
                    if (resolved != null) {
                        Log($"Probe '{localName}' -> '{resolved.FullName}'");
                        break;
                    }
                }

                if (resolved == null && m_parent != null)
                    resolved = m_parent.ProbeInNamespaces(localName);

                if(resolved == null)
                    Log($"Probe '{localName}' failed");

                m_cache[localName] = resolved;
            }

            return resolved;
        }

        Type Probe(string typeName) {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

            foreach (var assembly in assemblies) {
                var type = assembly.GetType(typeName);
                if (type != null)
                    return type;
            }
            return null;
        }
    }
}