<commit_before>﻿using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace DLToolkit.PageFactory
{
	public class XamarinFormsPageFactory : IPageFactory
	{
		readonly Dictionary<Type, IBasePage<INotifyPropertyChanged>> pageCache = new Dictionary<Type, IBasePage<INotifyPropertyChanged>>();

		readonly Dictionary<Type, Type> viewModelsTypes = new Dictionary<Type, Type>();

		readonly ConditionalWeakTable<INotifyPropertyChanged, IBasePage<INotifyPropertyChanged>> weakPageCache = new ConditionalWeakTable<INotifyPropertyChanged, IBasePage<INotifyPropertyChanged>>();

		public void AddToWeakCacheIfNotExists(IBasePage<INotifyPropertyChanged> page)
		{
			if (page.ViewModel == null)
				return;

			IBasePage<INotifyPropertyChanged> weakExists;

			if (!weakPageCache.TryGetValue(page.ViewModel, out weakExists))
			{
				weakPageCache.Add(page.ViewModel, page);
			}
		}

		public void RemoveFromWeakCacheIfExists(IBasePage<INotifyPropertyChanged> page)
		{
			if (page.ViewModel == null)
				return;
			
			IBasePage<INotifyPropertyChanged> weakExists;

			if (weakPageCache.TryGetValue(page.ViewModel, out weakExists))
			{
				weakPageCache.Remove(page.ViewModel);
			}
		}
			
		public NavigationPage Init<TMainPageViewModel, TNavigationPage>(params Assembly[] additionalPagesAssemblies) where TMainPageViewModel : class, INotifyPropertyChanged where TNavigationPage : NavigationPage, IBasePage<INotifyPropertyChanged>
		{
			PF.SetPageFactory(this);

			viewModelsTypes.Clear();

			var pagesAssemblies = additionalPagesAssemblies.ToList();
			pagesAssemblies.Add(typeof(TMainPageViewModel).GetTypeInfo().Assembly);
			pagesAssemblies.Add(typeof(TNavigationPage).GetTypeInfo().Assembly);

			foreach (var assembly in pagesAssemblies.Distinct())
			{
				foreach(var typeInfo in assembly.DefinedTypes.Where(t => t.IsClass && !t.IsAbstract && t.ImplementedInterfaces != null))
				{
					var found = typeInfo.ImplementedInterfaces.FirstOrDefault(t => t.IsConstructedGenericType && 
						t.GetGenericTypeDefinition() == typeof(IBasePage<>));

					if (found != default(Type))
					{
						var viewModelType = found.GenericTypeArguments.First();
						var pageType = typeInfo.AsType();

						if(!viewModelsTypes.ContainsKey(viewModelType))
						{
							var pageParameterlessCtors = (typeInfo.DeclaredConstructors
								.Where(c => c.IsPublic && c.GetParameters().Length == 0));

							if (!pageParameterlessCtors.Any())
								throw new ArgumentException(string.Format("Page {0} must have a public parameterless constructor", pageType));

							var viewModelTypeInfo = viewModelType.GetTypeInfo();

							var parameterlessCtors = (viewModelTypeInfo.DeclaredConstructors
								.Where(c => c.IsPublic && c.GetParameters().Length == 0));

							if (!parameterlessCtors.Any())
							{
								var ovViewModelInitializer = pageType.GetRuntimeMethods()
									.FirstOrDefault(v => v.Name.Equals("ViewModelInitializer", StringComparison.OrdinalIgnoreCase));

								var overridesViewModelInitializer = ovViewModelInitializer != null &&
									ovViewModelInitializer.DeclaringType != ovViewModelInitializer.GetRuntimeBaseDefinition().DeclaringType;

								if (!overridesViewModelInitializer)
									throw new ArgumentException(string.Format("ViewModel '{0}' must have a public parameterless constructor OR Page {1} must override ViewModelInitializer method", 
										viewModelType, pageType));
							}

							viewModelsTypes.Add(viewModelType, pageType);
						}
						else
						{
							throw new ArgumentOutOfRangeException(
								string.Format("ViewModel {0} has multiple Page definitions", viewModelType.ToString()));
						}
					}
				}	
			}

			var page = GetPageFromCache(typeof(TMainPageViewModel));
			navigationPage = (NavigationPage)Activator.CreateInstance(typeof(TNavigationPage), page, true);

			var baseNavPage = navigationPage as IBasePage<INotifyPropertyChanged>;
			if (baseNavPage != null)
			{
				ReplacePageViewModel(baseNavPage, baseNavPage.ViewModelInitializer());
			}

			return NavigationPage;
		}

		NavigationPage navigationPage = null;
		public NavigationPage NavigationPage
		{<commit_msg>Added pages inheritance support. Now, ViewModels use most inherited in hierarchy Page classes.<commit_after>﻿using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace DLToolkit.PageFactory
{
	public class XamarinFormsPageFactory : IPageFactory
	{
		readonly Dictionary<Type, IBasePage<INotifyPropertyChanged>> pageCache = new Dictionary<Type, IBasePage<INotifyPropertyChanged>>();

		readonly Dictionary<Type, Type> viewModelsTypes = new Dictionary<Type, Type>();

		readonly ConditionalWeakTable<INotifyPropertyChanged, IBasePage<INotifyPropertyChanged>> weakPageCache = new ConditionalWeakTable<INotifyPropertyChanged, IBasePage<INotifyPropertyChanged>>();

		public void AddToWeakCacheIfNotExists(IBasePage<INotifyPropertyChanged> page)
		{
			if (page.ViewModel == null)
				return;

			IBasePage<INotifyPropertyChanged> weakExists;

			if (!weakPageCache.TryGetValue(page.ViewModel, out weakExists))
			{
				weakPageCache.Add(page.ViewModel, page);
			}
		}

		public void RemoveFromWeakCacheIfExists(IBasePage<INotifyPropertyChanged> page)
		{
			if (page.ViewModel == null)
				return;
			
			IBasePage<INotifyPropertyChanged> weakExists;

			if (weakPageCache.TryGetValue(page.ViewModel, out weakExists))
			{
				weakPageCache.Remove(page.ViewModel);
			}
		}
			
		public NavigationPage Init<TMainPageViewModel, TNavigationPage>(params Assembly[] additionalPagesAssemblies) where TMainPageViewModel : class, INotifyPropertyChanged where TNavigationPage : NavigationPage, IBasePage<INotifyPropertyChanged>
		{
			PF.SetPageFactory(this);

			viewModelsTypes.Clear();

			var pagesAssemblies = additionalPagesAssemblies.ToList();
			pagesAssemblies.Add(typeof(TMainPageViewModel).GetTypeInfo().Assembly);
			pagesAssemblies.Add(typeof(TNavigationPage).GetTypeInfo().Assembly);

			foreach (var assembly in pagesAssemblies.Distinct())
			{
				foreach(var pageTypeInfo in assembly.DefinedTypes.Where(t => t.IsClass && !t.IsAbstract && t.ImplementedInterfaces != null))
				{
					var found = pageTypeInfo.ImplementedInterfaces.FirstOrDefault(t => t.IsConstructedGenericType && 
						t.GetGenericTypeDefinition() == typeof(IBasePage<>));

					if (found != default(Type))
					{
						var viewModelType = found.GenericTypeArguments.First();
						var viewModelTypeInfo = viewModelType.GetTypeInfo();
						var pageType = pageTypeInfo.AsType();

						var pageParameterlessCtors = (pageTypeInfo.DeclaredConstructors
							.Where(c => c.IsPublic && c.GetParameters().Length == 0));

						if (!pageParameterlessCtors.Any())
							throw new Exception(string.Format("Page {0} must have a public parameterless constructor", pageType));

						var parameterlessCtors = (viewModelTypeInfo.DeclaredConstructors
							.Where(c => c.IsPublic && c.GetParameters().Length == 0));

						if (!parameterlessCtors.Any())
						{
							var ovViewModelInitializer = pageType.GetRuntimeMethods()
								.FirstOrDefault(v => v.Name.Equals("ViewModelInitializer", StringComparison.OrdinalIgnoreCase));

							var overridesViewModelInitializer = ovViewModelInitializer != null &&
								ovViewModelInitializer.DeclaringType != ovViewModelInitializer.GetRuntimeBaseDefinition().DeclaringType;

							if (!overridesViewModelInitializer)
								throw new Exception(string.Format("ViewModel {0} must have a public parameterless constructor OR Page {1} must override ViewModelInitializer method", 
									viewModelType, pageType));
						}

						if(!viewModelsTypes.ContainsKey(viewModelType))
						{
							viewModelsTypes.Add(viewModelType, pageType);
						}
						else
						{
							var oldPageType = viewModelsTypes[viewModelType];

							if (pageTypeInfo.IsSubclassOf(oldPageType))
							{
								viewModelsTypes.Remove(viewModelType);
								viewModelsTypes.Add(viewModelType, pageType);
							}
						}
					}
				}	
			}

			var page = GetPageFromCache(typeof(TMainPageViewModel));
			navigationPage = (NavigationPage)Activator.CreateInstance(typeof(TNavigationPage), page, true);

			var baseNavPage = navigationPage as IBasePage<INotifyPropertyChanged>;
			if (baseNavPage != null)
			{
				ReplacePageViewModel(baseNavPage, baseNavPage.ViewModelInitializer());
			}

			return NavigationPage;
		}

		NavigationPage navigationPage = null;
		public NavigationPage NavigationPage
		{
{'fraction_non_alphanumeric': 0.09728558394160584, 'fraction_numerical': 0.0012545620437956204, 'mean_word_length': 4.596043395022336, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 49, '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': '3517460', 'n_tokens_mistral': 3091, 'n_tokens_neox': 2419, 'n_words': 464}