/* * Copyright © 2011, Petro Protsyk, Denys Vuika * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Scripting.SSharp.Diagnostics; using Scripting.SSharp.Runtime.Configuration; namespace Scripting.SSharp.Runtime { public class BaseAssemblyManager : IAssemblyManager { #region Properties /// /// List of assemblies which are in use for the current moment. /// NOTE: This list may be changed during run-time (new assemblies may be added, some of them may be removed) /// protected readonly List WorkingAssemblies = new List(); /// /// Types cache. Contains all loaded types /// protected readonly Dictionary Types = new Dictionary(); /// /// Types by short names /// protected readonly Dictionary ShortTypes = new Dictionary(); /// /// Cache of Namesapces /// protected readonly Dictionary> Namespaces = new Dictionary>(); protected ScriptConfiguration Configuration { get; private set; } #endregion #region Initialization [Promote(false)] public BaseAssemblyManager() { } [Promote(false)] public virtual void Initialize(ScriptConfiguration configuration) { Requires.NotNull(configuration, "configuration"); Configuration = configuration; FindAliasTypes(); LoadAssemblies(); ScanAssemblies(); } #endregion #region Methods public virtual IEnumerable GetExtensionMethods(Type type) { return Enumerable.Empty(); } public virtual IEnumerable GetExtensionMethods(Type type, string methodName) { return GetExtensionMethods(type).Where(m => m.Name == methodName); } /// /// Loads assemblies from configuration to memory and generate /// LoadedAssemblies list which will be scanned for types /// protected virtual void LoadAssemblies() { if (Configuration == null) return; foreach (Reference reference in Configuration.References) { Assembly assembly = reference.Load(); if (WorkingAssemblies.Contains(assembly)) throw new NotSupportedException("Duplicate assembly in configuration"); WorkingAssemblies.Add(assembly); } } /// /// Scans types in Loaded assemblies /// protected virtual void ScanAssemblies() { foreach (Assembly assembly in WorkingAssemblies.ToArray()) AddAssembly(assembly); } protected virtual void RegisterType(Type type) { Requires.NotNull(type, "type"); if (!Types.ContainsKey(type.FullName)) { Types.Add(type.FullName, type); //Register in namespaces if (!string.IsNullOrEmpty(type.Namespace)) { string[] parts = type.Namespace.Split('.'); string current = null; List types = null; foreach (string part in parts) { if (current == null) current = part; else current += "." + part; if (!Namespaces.TryGetValue(current, out types)) { types = new List(); Namespaces.Add(current, types); } } // Add type to the last namespace types.Add(type); } } if (!ShortTypes.ContainsKey(type.Name)) { ShortTypes.Add(type.Name, type); } } protected virtual void UnRegisterType(Type type) { Requires.NotNull(type, "type"); if (Types.ContainsKey(type.FullName)) { Types.Remove(type.FullName); //Clear namespace cache if (!string.IsNullOrEmpty(type.Namespace)) { List types = Namespaces[type.Namespace]; types.Remove(type); // Remove all empty subnamespaces string[] parts = type.Namespace.Split('.'); string current = null; for (int i = 0; i < parts.Length; i++) { current = string.Join(".", parts.Take(parts.Length - i)); types = Namespaces[current]; if (types.Count == 0 && Namespaces.Keys.Where(p=>p.Contains(current+".")).Count()==0) { Namespaces.Remove(type.Namespace); } } } } if (ShortTypes.ContainsKey(type.Name)) { ShortTypes.Remove(type.Name); } //Remove all aliases if (ShortTypes.ContainsValue(type)) { var keysToRemove = (from value in ShortTypes where value.Value == type select value.Key).ToList(); foreach (var key in keysToRemove) ShortTypes.Remove(key); } } private void FindAliasTypes() { if (Configuration == null) return; foreach (TypeXml typeXml in Configuration.Types) { Type type = RuntimeHost.GetNativeType(typeXml.QualifiedName); if (type == null) throw new NullReferenceException(string.Format("Type {0} is not found", typeXml.QualifiedName)); AddType(typeXml.Alias, type); } } protected virtual AssemblyHandlerEventArgs OnBeforeAddAssembly(Assembly assembly) { Requires.NotNull(assembly, "assembly"); var args = new AssemblyHandlerEventArgs(assembly); var hanlder = BeforeAddAssembly; if (hanlder != null) hanlder(this, args); return args; } public event EventHandler BeforeAddAssembly; protected virtual AssemblyTypeHandlerEventArgs OnBeforeAddType(Assembly assembly, Type type) { Requires.NotNull(assembly, "assembly"); Requires.NotNull(type, "type"); var args = new AssemblyTypeHandlerEventArgs(assembly, type); var handler = BeforeAddType; if (handler != null) handler(this, args); return args; } public event EventHandler BeforeAddType; #endregion #region Public Interface public virtual void AddAssembly(Assembly assembly) { Requires.NotNull(assembly, "assembly"); // Skip processing dynamic assemblies if (assembly.IsDynamic) return; if (OnBeforeAddAssembly(assembly).Cancel) { WorkingAssemblies.Remove(assembly); return; } if (!WorkingAssemblies.Contains(assembly)) { WorkingAssemblies.Add(assembly); } foreach (Type type in assembly.GetExportedTypes()) { if (!type.IsPublic) continue; if (OnBeforeAddType(assembly, type).Cancel) continue; RegisterType(type); } } public virtual void RemoveAssembly(Assembly assembly) { Requires.NotNull(assembly, "assembly"); if (!WorkingAssemblies.Contains(assembly)) return; foreach (Type type in assembly.GetExportedTypes()) { if (!type.IsPublic) continue; UnRegisterType(type); } WorkingAssemblies.Remove(assembly); } /// /// Returns type by given name /// /// Short, Alias or FullType name /// Type /// /// If type not found /// public Type GetType(string name) { Requires.NotNullOrEmpty(name, "name"); Type result; if (ShortTypes.TryGetValue(name, out result)) return result; if (Types.TryGetValue(name, out result)) return result; throw new ScriptIdNotFoundException(string.Format(Strings.TypeNotFoundError, name)); } public bool HasType(string name) { Requires.NotNullOrEmpty(name, "name"); return ShortTypes.ContainsKey(name) || Types.ContainsKey(name); } /// /// Adds type to a manager /// /// /// public void AddType(string alias, Type type) { Requires.NotNullOrEmpty(alias, "alias"); Requires.NotNull(type, "type"); RuntimeHost.Lock(); try { if (ShortTypes.ContainsKey(alias)) { ShortTypes[alias] = type; } else { ShortTypes.Add(alias, type); } } finally { RuntimeHost.UnLock(); } } public bool HasNamespace(string name) { Requires.NotNullOrEmpty(name, "name"); return Namespaces.ContainsKey(name); } #endregion #region IDisposable Members [Promote(false)] public virtual void Dispose() { ShortTypes.Clear(); Types.Clear(); } #endregion } }