<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using N2.Persistence;
using System.Reflection;

namespace N2.Engine
{
	/// <summary>
	/// Caches information about assemblies to increase startup performance.
	/// </summary>
	public class TypeCache
	{
		#region CachedAssembly
		class CachedAssembly
		{
			Logger<TypeCache> logger;
			public Assembly Assembly { get; set; }
			public Guid ModuleId;
			public Dictionary<string, List<Type>> Queries = new Dictionary<string, List<Type>>();

			public IEnumerable<Type> GetOrCreateCache(string key, Func<Assembly, IEnumerable<Type>> factory, ref bool factoryInvoked)
			{
				lock (this)
				{
					if (!Queries.ContainsKey(key))
					{
						factoryInvoked = true;
						var types = factory(Assembly).ToList();
						logger.DebugFormat("Recreating cache for key {0}, found {1} types in assembly {2}/{3}", key, types.Count, Assembly, ModuleId);
						Queries[key] = types;
						return types;
					}
					else
						return Queries[key];
				}
			}
		}
		#endregion

		Logger<TypeCache> logger;
		private BasicTemporaryFileHelper temp;
		private List<CachedAssembly> cache;

		public TypeCache(BasicTemporaryFileHelper temp)
		{
			this.temp = temp;
		}

		public IEnumerable<Type> GetTypes(string key, Func<IEnumerable<Assembly>> assemblyFactory, Func<Assembly, IEnumerable<Type>> factory)
		{
			bool changesMade = false;
			foreach (var cachedAssembly in GetCache(assemblyFactory))
			{
				foreach (var type in cachedAssembly.GetOrCreateCache(key, factory, ref changesMade))
					yield return type;
			}

			if (changesMade)
				SaveCache();
		}

		/// <summary>
		/// Gets assemblies from cached location. If files in the probing paths have changed assemblies are re-read.
		/// </summary>
		/// <param name="factory"></param>
		/// <returns></returns>
		public virtual IEnumerable<Assembly> GetAssemblies(Func<IEnumerable<Assembly>> factory)
		{
			return GetCache(factory).Select(a => a.Assembly);
		}

		private IEnumerable<CachedAssembly> GetCache(Func<IEnumerable<Assembly>> factory)
		{
			if (cache != null)
				return cache;

			var path = GetAssemblyCachePath();

			var assemblies = factory().Select(a => new CachedAssembly { Assembly = a, ModuleId = a.ManifestModule.ModuleVersionId }).ToList();
			if (File.Exists(path))
			{
				lock (this)
				{
					if (cache != null)
						return cache;<commit_msg>Handled exception saving assembly cache (fixes #219)<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using N2.Persistence;
using System.Reflection;

namespace N2.Engine
{
	/// <summary>
	/// Caches information about assemblies to increase startup performance.
	/// </summary>
	public class TypeCache
	{
		#region CachedAssembly
		class CachedAssembly
		{
			Logger<TypeCache> logger;
			public Assembly Assembly { get; set; }
			public Guid ModuleId;
			public Dictionary<string, List<Type>> Queries = new Dictionary<string, List<Type>>();

			public IEnumerable<Type> GetOrCreateCache(string key, Func<Assembly, IEnumerable<Type>> factory, ref bool factoryInvoked)
			{
				lock (this)
				{
					if (!Queries.ContainsKey(key))
					{
						factoryInvoked = true;
						var types = factory(Assembly).ToList();
						logger.DebugFormat("Recreating cache for key {0}, found {1} types in assembly {2}/{3}", key, types.Count, Assembly, ModuleId);
						Queries[key] = types;
						return types;
					}
					else
						return Queries[key];
				}
			}
		}
		#endregion

		Logger<TypeCache> logger;
		private BasicTemporaryFileHelper temp;
		private List<CachedAssembly> cache;

		public TypeCache(BasicTemporaryFileHelper temp)
		{
			this.temp = temp;
		}

		public IEnumerable<Type> GetTypes(string key, Func<IEnumerable<Assembly>> assemblyFactory, Func<Assembly, IEnumerable<Type>> factory)
		{
			bool changesMade = false;
			foreach (var cachedAssembly in GetCache(assemblyFactory))
			{
				foreach (var type in cachedAssembly.GetOrCreateCache(key, factory, ref changesMade))
					yield return type;
			}

			if (changesMade)
			{
				try
				{
					SaveCache();
				}
				catch (Exception ex)
				{
					logger.Error("Error saving assembly cache", ex);
				}
			}
		}

		/// <summary>
		/// Gets assemblies from cached location. If files in the probing paths have changed assemblies are re-read.
		/// </summary>
		/// <param name="factory"></param>
		/// <returns></returns>
		public virtual IEnumerable<Assembly> GetAssemblies(Func<IEnumerable<Assembly>> factory)
		{
			return GetCache(factory).Select(a => a.Assembly);
		}

		private IEnumerable<CachedAssembly> GetCache(Func<IEnumerable<Assembly>> factory)
		{
			if (cache != null)
				return cache;

			var path = GetAssemblyCachePath();

			var assemblies = factory().Select(a => new CachedAssembly { Assembly = a, ModuleId = a.ManifestModule.ModuleVersionId }).ToList();
			if (File.Exists(path))
			{
				lock (this)
				{
					if (cache != null)
						return cache;
{'fraction_non_alphanumeric': 0.11603417444863898, 'fraction_numerical': 0.0029803298231670973, 'mean_word_length': 3.5888787602552417, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7374446', 'n_tokens_mistral': 1840, 'n_tokens_neox': 1478, 'n_words': 406}