context: using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Reflection;
  using NHibernate.Cfg.MappingSchema;
  using NHibernate.Mapping.ByCode.Impl;
  using NHibernate.Mapping.ByCode.Impl.CustomizersImpl;
  
  namespace NHibernate.Mapping.ByCode
  {
  	public class ModelMapper
  	{
  		private readonly ICustomizersHolder customizerHolder;
  		private readonly IModelExplicitDeclarationsHolder explicitDeclarationsHolder;
  		private readonly ICandidatePersistentMembersProvider membersProvider;
  		private readonly IModelInspector modelInspector;
  		private readonly List<Import> imports = new List<Import>();
  
  		public ModelMapper() : this(new ExplicitlyDeclaredModel()) { }
  
  		public ModelMapper(IModelInspector modelInspector) : this(modelInspector, modelInspector as IModelExplicitDeclarationsHolder) {}
  
  		public ModelMapper(IModelInspector modelInspector, ICandidatePersistentMembersProvider membersProvider)
  			: this(modelInspector, modelInspector as IModelExplicitDeclarationsHolder, new CustomizersHolder(), membersProvider) {}
  
  		public ModelMapper(IModelInspector modelInspector, IModelExplicitDeclarationsHolder explicitDeclarationsHolder)
  			: this(modelInspector, explicitDeclarationsHolder, new CustomizersHolder(), new DefaultCandidatePersistentMembersProvider()) {}
  
  		public ModelMapper(IModelInspector modelInspector, IModelExplicitDeclarationsHolder explicitDeclarationsHolder, ICustomizersHolder customizerHolder,
  		                   ICandidatePersistentMembersProvider membersProvider)
  		{
  			if (modelInspector == null)
  			{
  				throw new ArgumentNullException("modelInspector");
  			}
  			if (customizerHolder == null)
  			{
  				throw new ArgumentNullException("customizerHolder");
  			}
  			if (membersProvider == null)
  			{
  				throw new ArgumentNullException("membersProvider");
  			}
  			this.modelInspector = modelInspector;
  			this.customizerHolder = customizerHolder;
  			this.explicitDeclarationsHolder = explicitDeclarationsHolder ?? new FakeModelExplicitDeclarationsHolder();
  			this.membersProvider = membersProvider;
  		}
  
  		#region Events
  
  		/// <summary>
  		/// Occurs before apply pattern-appliers on a root class.
  		/// </summary>
  		public event RootClassMappingHandler BeforeMapClass;
  
  		/// <summary>
  		/// Occurs before apply pattern-appliers on a subclass.
  		/// </summary>
  		public event SubclassMappingHandler BeforeMapSubclass;
  
  		/// <summary>
  		/// Occurs before apply pattern-appliers on a joined-subclass.
  		/// </summary>
  		public event JoinedSubclassMappingHandler BeforeMapJoinedSubclass;
  
  		/// <summary>
  		/// Occurs before apply pattern-appliers on a union-subclass.
  		/// </summary>
  		public event UnionSubclassMappingHandler BeforeMapUnionSubclass;
  
  		public event PropertyMappingHandler BeforeMapProperty;
  
  		public event ManyToOneMappingHandler BeforeMapManyToOne;
  
  		public event OneToOneMappingHandler BeforeMapOneToOne;
  
  		public event AnyMappingHandler BeforeMapAny;
  
  		public event ComponentMappingHandler BeforeMapComponent;
  
  		public event SetMappingHandler BeforeMapSet;
  
  		public event BagMappingHandler BeforeMapBag;
  
  		public event IdBagMappingHandler BeforeMapIdBag;
  
  		public event ListMappingHandler BeforeMapList;
  
  		public event MapMappingHandler BeforeMapMap;
  
  		public event ManyToManyMappingHandler BeforeMapManyToMany;
  
  		public event ElementMappingHandler BeforeMapElement;
  
  		public event OneToManyMappingHandler BeforeMapOneToMany;
  
  		public event MapKeyManyToManyMappingHandler BeforeMapMapKeyManyToMany;
  		public event MapKeyMappingHandler BeforeMapMapKey;
  
  		/// <summary>
  		/// Occurs after apply the last customizer on a root class.
  		/// </summary>
  		public event RootClassMappingHandler AfterMapClass;
  
  		/// <summary>
  		/// Occurs after apply the last customizer on a subclass.
  		/// </summary>
  		public event SubclassMappingHandler AfterMapSubclass;
  
  		/// <summary>
  		/// Occurs after apply the last customizer on a joined-subclass..
  		/// </summary>
  		public event JoinedSubclassMappingHandler AfterMapJoinedSubclass;
  
  		/// <summary>
  		/// Occurs after apply the last customizer on a union-subclass..
  		/// </summary>
  		public event UnionSubclassMappingHandler AfterMapUnionSubclass;
  
  		public event PropertyMappingHandler AfterMapProperty;
  
  		public event ManyToOneMappingHandler AfterMapManyToOne;
  
  		public event OneToOneMappingHandler AfterMapOneToOne;
  
  		public event AnyMappingHandler AfterMapAny;
  
  		public event ComponentMappingHandler AfterMapComponent;
  
  		public event SetMappingHandler AfterMapSet;
  
  		public event BagMappingHandler AfterMapBag;
  
  		public event IdBagMappingHandler AfterMapIdBag;
  
  		public event ListMappingHandler AfterMapList;
  
  		public event MapMappingHandler AfterMapMap;
  
  		public event ManyToManyMappingHandler AfterMapManyToMany;
  
  		public event ElementMappingHandler AfterMapElement;
  
  		public event OneToManyMappingHandler AfterMapOneToMany;
  
  		public event MapKeyManyToManyMappingHandler AfterMapMapKeyManyToMany;
  
  		public event MapKeyMappingHandler AfterMapMapKey;
  
  		private void InvokeBeforeMapUnionSubclass(System.Type type, IUnionSubclassAttributesMapper unionsubclasscustomizer)
  		{
  			UnionSubclassMappingHandler handler = BeforeMapUnionSubclass;
  			if (handler != null)
  			{
  				handler(ModelInspector, type, unionsubclasscustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapJoinedSubclass(System.Type type, IJoinedSubclassAttributesMapper joinedsubclasscustomizer)
  		{
  			JoinedSubclassMappingHandler handler = BeforeMapJoinedSubclass;
  			if (handler != null)
  			{
  				handler(ModelInspector, type, joinedsubclasscustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapSubclass(System.Type type, ISubclassAttributesMapper subclasscustomizer)
  		{
  			SubclassMappingHandler handler = BeforeMapSubclass;
  			if (handler != null)
  			{
  				handler(ModelInspector, type, subclasscustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapClass(System.Type type, IClassAttributesMapper classcustomizer)
  		{
  			RootClassMappingHandler handler = BeforeMapClass;
  			if (handler != null)
  			{
  				handler(ModelInspector, type, classcustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapProperty(PropertyPath member, IPropertyMapper propertycustomizer)
  		{
  			PropertyMappingHandler handler = BeforeMapProperty;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapManyToOne(PropertyPath member, IManyToOneMapper propertycustomizer)
  		{
  			ManyToOneMappingHandler handler = BeforeMapManyToOne;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapOneToOne(PropertyPath member, IOneToOneMapper propertycustomizer)
  		{
  			OneToOneMappingHandler handler = BeforeMapOneToOne;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapAny(PropertyPath member, IAnyMapper propertycustomizer)
  		{
  			AnyMappingHandler handler = BeforeMapAny;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapComponent(PropertyPath member, IComponentAttributesMapper propertycustomizer)
  		{
  			ComponentMappingHandler handler = BeforeMapComponent;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapSet(PropertyPath member, ISetPropertiesMapper propertycustomizer)
  		{
  			SetMappingHandler handler = BeforeMapSet;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapBag(PropertyPath member, IBagPropertiesMapper propertycustomizer)
  		{
  			BagMappingHandler handler = BeforeMapBag;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapIdBag(PropertyPath member, IIdBagPropertiesMapper propertycustomizer)
  		{
  			IdBagMappingHandler handler = BeforeMapIdBag;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapList(PropertyPath member, IListPropertiesMapper propertycustomizer)
  		{
  			ListMappingHandler handler = BeforeMapList;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapMap(PropertyPath member, IMapPropertiesMapper propertycustomizer)
  		{
  			MapMappingHandler handler = BeforeMapMap;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapManyToMany(PropertyPath member, IManyToManyMapper collectionrelationmanytomanycustomizer)
  		{
  			ManyToManyMappingHandler handler = BeforeMapManyToMany;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, collectionrelationmanytomanycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapElement(PropertyPath member, IElementMapper collectionrelationelementcustomizer)
  		{
  			ElementMappingHandler handler = BeforeMapElement;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, collectionrelationelementcustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapOneToMany(PropertyPath member, IOneToManyMapper collectionrelationonetomanycustomizer)
  		{
  			OneToManyMappingHandler handler = BeforeMapOneToMany;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, collectionrelationonetomanycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapMapKeyManyToMany(PropertyPath member, IMapKeyManyToManyMapper mapkeymanytomanycustomizer)
  		{
  			MapKeyManyToManyMappingHandler handler = BeforeMapMapKeyManyToMany;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, mapkeymanytomanycustomizer);
  			}
  		}
  
  		private void InvokeBeforeMapMapKey(PropertyPath member, IMapKeyMapper mapkeyelementcustomizer)
  		{
  			MapKeyMappingHandler handler = BeforeMapMapKey;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, mapkeyelementcustomizer);
  			}
  		}
  
  		private void InvokeAfterMapUnionSubclass(System.Type type, IUnionSubclassAttributesMapper unionsubclasscustomizer)
  		{
  			UnionSubclassMappingHandler handler = AfterMapUnionSubclass;
  			if (handler != null)
  			{
  				handler(ModelInspector, type, unionsubclasscustomizer);
  			}
  		}
  
  		private void InvokeAfterMapJoinedSubclass(System.Type type, IJoinedSubclassAttributesMapper joinedsubclasscustomizer)
  		{
  			JoinedSubclassMappingHandler handler = AfterMapJoinedSubclass;
  			if (handler != null)
  			{
  				handler(ModelInspector, type, joinedsubclasscustomizer);
  			}
  		}
  
  		private void InvokeAfterMapSubclass(System.Type type, ISubclassAttributesMapper subclasscustomizer)
  		{
  			SubclassMappingHandler handler = AfterMapSubclass;
  			if (handler != null)
  			{
  				handler(ModelInspector, type, subclasscustomizer);
  			}
  		}
  
  		private void InvokeAfterMapClass(System.Type type, IClassAttributesMapper classcustomizer)
  		{
  			RootClassMappingHandler handler = AfterMapClass;
  			if (handler != null)
  			{
  				handler(ModelInspector, type, classcustomizer);
  			}
  		}
  
  		private void InvokeAfterMapProperty(PropertyPath member, IPropertyMapper propertycustomizer)
  		{
  			PropertyMappingHandler handler = AfterMapProperty;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapManyToOne(PropertyPath member, IManyToOneMapper propertycustomizer)
  		{
  			ManyToOneMappingHandler handler = AfterMapManyToOne;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapOneToOne(PropertyPath member, IOneToOneMapper propertycustomizer)
  		{
  			OneToOneMappingHandler handler = AfterMapOneToOne;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapAny(PropertyPath member, IAnyMapper propertycustomizer)
  		{
  			AnyMappingHandler handler = AfterMapAny;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapComponent(PropertyPath member, IComponentAttributesMapper propertycustomizer)
  		{
  			ComponentMappingHandler handler = AfterMapComponent;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapSet(PropertyPath member, ISetPropertiesMapper propertycustomizer)
  		{
  			SetMappingHandler handler = AfterMapSet;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapBag(PropertyPath member, IBagPropertiesMapper propertycustomizer)
  		{
  			BagMappingHandler handler = AfterMapBag;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapIdBag(PropertyPath member, IIdBagPropertiesMapper propertycustomizer)
  		{
  			IdBagMappingHandler handler = AfterMapIdBag;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapList(PropertyPath member, IListPropertiesMapper propertycustomizer)
  		{
  			ListMappingHandler handler = AfterMapList;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapMap(PropertyPath member, IMapPropertiesMapper propertycustomizer)
  		{
  			MapMappingHandler handler = AfterMapMap;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, propertycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapManyToMany(PropertyPath member, IManyToManyMapper collectionrelationmanytomanycustomizer)
  		{
  			ManyToManyMappingHandler handler = AfterMapManyToMany;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, collectionrelationmanytomanycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapElement(PropertyPath member, IElementMapper collectionrelationelementcustomizer)
  		{
  			ElementMappingHandler handler = AfterMapElement;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, collectionrelationelementcustomizer);
  			}
  		}
  
  		private void InvokeAfterMapOneToMany(PropertyPath member, IOneToManyMapper collectionrelationonetomanycustomizer)
  		{
  			OneToManyMappingHandler handler = AfterMapOneToMany;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, collectionrelationonetomanycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapMapKeyManyToMany(PropertyPath member, IMapKeyManyToManyMapper mapkeymanytomanycustomizer)
  		{
  			MapKeyManyToManyMappingHandler handler = AfterMapMapKeyManyToMany;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, mapkeymanytomanycustomizer);
  			}
  		}
  
  		private void InvokeAfterMapMapKey(PropertyPath member, IMapKeyMapper mapkeyelementcustomizer)
  		{
  			MapKeyMappingHandler handler = AfterMapMapKey;
  			if (handler != null)
  			{
  				handler(ModelInspector, member, mapkeyelementcustomizer);
  			}
  		}
  
  		#endregion
  
  		public IModelInspector ModelInspector
  		{
  			get { return modelInspector; }
  		}
  
  		protected ICandidatePersistentMembersProvider MembersProvider
  		{
  			get { return membersProvider; }
  		}
  
  		public void Class<TRootEntity>(Action<IClassMapper<TRootEntity>> customizeAction) where TRootEntity : class
  		{
  			var customizer = new ClassCustomizer<TRootEntity>(explicitDeclarationsHolder, customizerHolder);
  			customizeAction(customizer);
  		}
  
  		public void Subclass<TEntity>(Action<ISubclassMapper<TEntity>> customizeAction) where TEntity : class
  		{
  			var customizer = new SubclassCustomizer<TEntity>(explicitDeclarationsHolder, customizerHolder);
  			customizeAction(customizer);
  		}
  
  		public void JoinedSubclass<TEntity>(Action<IJoinedSubclassMapper<TEntity>> customizeAction) where TEntity : class
  		{
  			var customizer = new JoinedSubclassCustomizer<TEntity>(explicitDeclarationsHolder, customizerHolder);
  			customizeAction(customizer);
  		}
  
  		public void UnionSubclass<TEntity>(Action<IUnionSubclassMapper<TEntity>> customizeAction) where TEntity : class
  		{
  			var customizer = new UnionSubclassCustomizer<TEntity>(explicitDeclarationsHolder, customizerHolder);
  			customizeAction(customizer);
  		}
  
  		public void Component<TComponent>(Action<IComponentMapper<TComponent>> customizeAction)
  		{
  			var customizer = new ComponentCustomizer<TComponent>(explicitDeclarationsHolder, customizerHolder);
  			customizeAction(customizer);
  		}
  
  		public void Import<TImportClass>()
  		{
  			Import<TImportClass>(typeof(TImportClass).Name);
  		}
  
  		public void Import<TImportClass>(string rename)
  		{
  			imports.Add(new Import(typeof(TImportClass), rename));
  		}
  
  		public HbmMapping CompileMappingFor(IEnumerable<System.Type> types)
  		{
  			if (types == null)
  			{
  				throw new ArgumentNullException("types");
  			}
  
  			var typeToMap = types.Distinct()
  								 .OrderBy(x => x, new TypeHierarchyComparer())
  								 .ToList();
  
  			string defaultAssemblyName = null;
  			string defaultNamespace = null;
  			System.Type firstType = typeToMap.FirstOrDefault();
  			if (firstType != null && typeToMap.All(t => t.Assembly.Equals(firstType.Assembly)))
  			{
  				//NH-2831: always use the full name of the assembly because it may come from GAC
  				defaultAssemblyName = firstType.Assembly.GetName().FullName;
  			}
  			if (firstType != null && typeToMap.All(t => t.Namespace == firstType.Namespace))
  			{
  				defaultNamespace = firstType.Namespace;
  			}
  			var mapping = NewHbmMapping(defaultAssemblyName, defaultNamespace);
  			foreach (var type in RootClasses(typeToMap))
  			{
  				MapRootClass(type, mapping);
  			}
  			foreach (var type in Subclasses(typeToMap))
  			{
  				AddSubclassMapping(mapping, type);
  			}
  			return mapping;
  		}
  
  		public IEnumerable<HbmMapping> CompileMappingForEach(IEnumerable<System.Type> types)
  		{
  			if (types == null)
  			{
  				throw new ArgumentNullException("types");
  			}
  			var typeToMap = types.Distinct()
  								 .OrderBy(x => x, new TypeHierarchyComparer())
  								 .ToList();
  
  			//NH-2831: always use the full name of the assembly because it may come from GAC
  			foreach (var type in RootClasses(typeToMap))
  			{
  				var mapping = NewHbmMapping(type.Assembly.GetName().FullName, type.Namespace);
  				MapRootClass(type, mapping);
  				yield return mapping;
  			}
  			foreach (var type in Subclasses(typeToMap))
  			{
  				var mapping = NewHbmMapping(type.Assembly.GetName().FullName, type.Namespace);
  				AddSubclassMapping(mapping, type);
  				yield return mapping;
  			}
  		}
  
  		private HbmMapping NewHbmMapping(string defaultAssemblyName, string defaultNamespace)
  		{
  			var hbmMapping = new HbmMapping {assembly = defaultAssemblyName, @namespace = defaultNamespace};
  
  			imports.ForEach(i => i.AddToMapping(hbmMapping));
  			imports.Clear();
  
  			return hbmMapping;
  		}
  
  		private IEnumerable<System.Type> Subclasses(IEnumerable<System.Type> types)
  		{
  			return types.Where(type => modelInspector.IsEntity(type) && !modelInspector.IsRootEntity(type));
  		}
  
  		private IEnumerable<System.Type> RootClasses(IEnumerable<System.Type> types)
  		{
  			return types.Where(type => modelInspector.IsEntity(type) && modelInspector.IsRootEntity(type));
  		}
  
  		private void AddSubclassMapping(HbmMapping mapping, System.Type type)
  		{
  			if (modelInspector.IsTablePerClassHierarchy(type))
  			{
  				MapSubclass(type, mapping);
  			}
  
gt: 			else if (modelInspector.IsTablePerClass(type))