/// <remarks>
		/// Called for all collections
		/// </remarks>
		public static void BindCollection( XmlNode node, Mapping.Collection model, string className, string path, Mappings mappings )
		{
			//ROLENAME
			model.Role = StringHelper.Qualify( className, path );

			XmlAttribute inverseNode = node.Attributes[ "inverse" ];
			if( inverseNode != null )
			{
				model.IsInverse = StringHelper.BooleanValue( inverseNode.Value );
			}

			XmlAttribute orderNode = node.Attributes[ "order-by" ];
			if( orderNode != null )
			{
				model.OrderBy = orderNode.Value;
			}
			XmlAttribute whereNode = node.Attributes[ "where" ];
			if( whereNode != null )
			{
				model.Where = whereNode.Value;
			}
			XmlAttribute batchNode = node.Attributes[ "batch-size" ];
			if( batchNode != null )
			{
				model.BatchSize = Int32.Parse( batchNode.Value );
			}

			//PERSISTER
			XmlAttribute persisterNode = node.Attributes[ "persister" ];
			if( persisterNode == null )
			{
				//persister = CollectionPersisterImpl.class;
			}
			else
			{
				model.CollectionPersisterClass = ClassForNameChecked(
					persisterNode.Value, mappings,
					"could not instantiate collection persister class: {0}" );
			}

			InitOuterJoinFetchSetting( node, model );

			XmlNode oneToManyNode = node.SelectSingleNode( nsOneToMany, nsmgr );
			if( oneToManyNode != null )
			{
				OneToMany oneToMany = new OneToMany( model.Owner );
				model.Element = oneToMany;
				BindOneToMany( oneToManyNode, oneToMany, mappings );
				//we have to set up the table later!! yuck
			}
			else
			{
				//TABLE
				XmlAttribute tableNode = node.Attributes[ "table" ];
				string tableName;
				if( tableNode != null )
				{
					tableName = mappings.NamingStrategy.TableName( tableNode.Value );
				}
				else
				{
					tableName = mappings.NamingStrategy.PropertyToTableName( className, path );
				}
				XmlAttribute schemaNode = node.Attributes[ "schema" ];
				string schema = schemaNode == null ? mappings.SchemaName : schemaNode.Value;
				model.CollectionTable = mappings.AddTable( schema, tableName );

				log.Info( "Mapping collection: " + model.Role + " -> " + model.CollectionTable.Name );
			}

			//LAZINESS
			XmlAttribute lazyNode = node.Attributes[ "lazy" ];
			if( lazyNode != null )
			{
				model.IsLazy = StringHelper.BooleanValue( lazyNode.Value );
			}

			//SORT
			XmlAttribute sortedAtt = node.Attributes[ "sort" ];
			// unsorted, natural, comparator.class.name
			if( sortedAtt == null || sortedAtt.Value.Equals( "unsorted" ) )
			{
				model.IsSorted = false;
			}
			else
			{
				model.IsSorted = true;
				string comparatorClassName = FullClassName( sortedAtt.Value, mappings );
				if( !comparatorClassName.Equals( "natural" ) )
				{
					try
					{
						model.Comparer = ( IComparer ) Activator.CreateInstance( ReflectHelper.ClassForName( comparatorClassName ) );
					}
					catch
					{
						throw new MappingException( "could not instantiate comparer class: " + comparatorClassName );
					}
				}
			}

			//ORPHAN DELETE (used for programmer error detection)
			XmlAttribute cascadeAtt = node.Attributes[ "cascade" ];
			if( cascadeAtt != null && cascadeAtt.Value.Equals( "all-delete-orphan" ) )
			{
				model.OrphanDelete = true;
			}

			//set up second pass
			if( model is List )
			{
				mappings.AddSecondPass( new ListSecondPass( node, mappings, ( List ) model ) );
			}
			else if( model is Map )
			{
				mappings.AddSecondPass( new MapSecondPass( node, mappings, ( Map ) model ) );
			}
			else if( model is Set )
			{
				mappings.AddSecondPass( new SetSecondPass( node, mappings, ( Set ) model ) );
			}
			else if( model is IdentifierCollection )
			{
				mappings.AddSecondPass( new IdentifierCollectionSecondPass( node, mappings, ( IdentifierCollection ) model ) );
			}
			else
			{
				mappings.AddSecondPass( new CollectionSecondPass( node, mappings, model ) );
			}
		}
		public static void BindJoinedSubclass( XmlNode node, Subclass model, Mappings mappings )
		{
			BindClass( node, model, mappings );

			// joined subclass
			if( model.ClassPersisterClass == null )
			{
				model.RootClazz.ClassPersisterClass = typeof( NormalizedEntityPersister );
			}

			//table + schema names
			XmlAttribute schemaNode = node.Attributes[ "schema" ];
			string schema = schemaNode == null ? mappings.SchemaName : schemaNode.Value;
			Table mytable = mappings.AddTable( schema, GetClassTableName( model, node, mappings ) );
			model.Table = mytable;

			log.Info( "Mapping joined-subclass: " + model.Name + " -> " + model.Table.Name );

			XmlNode keyNode = node.SelectSingleNode( nsKey, nsmgr );
			SimpleValue key = new SimpleValue( mytable );
			model.Key = key;
			BindSimpleValue( keyNode, key, false, model.Name, mappings );

			model.Key.Type = model.Identifier.Type;
			model.CreatePrimaryKey( dialect );
			model.CreateForeignKey();

			// CHECK
			XmlAttribute chNode = node.Attributes[ "check" ];
			if( chNode != null )
			{
				mytable.AddCheckConstraint( chNode.Value );
			}

			// properties
			PropertiesFromXML( node, model, mappings );
		}
		public static void BindRootClass( XmlNode node, RootClass model, Mappings mappings )
		{
			BindClass( node, model, mappings );

			//TABLENAME
			XmlAttribute schemaNode = node.Attributes[ "schema" ];
			string schema = schemaNode == null ? mappings.SchemaName : schemaNode.Value;
			Table table = mappings.AddTable( schema, GetClassTableName( model, node, mappings ) );
			model.Table = table;

			log.Info( "Mapping class: " + model.Name + " -> " + model.Table.Name );

			//MUTABLE
			XmlAttribute mutableNode = node.Attributes[ "mutable" ];
			model.IsMutable = ( mutableNode == null ) || mutableNode.Value.Equals( "true" );

			//WHERE
			XmlAttribute whereNode = node.Attributes[ "where" ];
			if( whereNode != null )
			{
				model.Where = whereNode.Value;
			}

			//CHECK
			XmlAttribute checkNode = node.Attributes[ "check" ];
			if( checkNode != null )
			{
				table.AddCheckConstraint( checkNode.Value );
			}

			//POLYMORPHISM
			XmlAttribute polyNode = node.Attributes[ "polymorphism" ];
			model.IsExplicitPolymorphism = ( polyNode != null ) && polyNode.Value.Equals( "explicit" );

			foreach( XmlNode subnode in node.ChildNodes )
			{
				string name = subnode.LocalName; //Name;
				string propertyName = GetPropertyName( subnode );

				//I am only concerned with elements that are from the nhibernate namespace
				if( subnode.NamespaceURI != Configuration.MappingSchemaXMLNS )
				{
					continue;
				}

				switch( name )
				{
				case "id":
					SimpleValue id = new SimpleValue( table );
					model.Identifier = id;

					if( propertyName == null )
					{
						BindSimpleValue( subnode, id, false, RootClass.DefaultIdentifierColumnName, mappings );
						if( id.Type == null )
						{
							throw new MappingException( "must specify an identifier type: " + model.MappedClass.Name );
						}
						model.IdentifierProperty = null;
					}
					else
					{
						BindSimpleValue( subnode, id, false, propertyName, mappings );
						id.SetTypeByReflection( model.MappedClass, propertyName, PropertyAccess( subnode, mappings ) );
						Mapping.Property prop = new Mapping.Property( id );
						BindProperty( subnode, prop, mappings );
						model.IdentifierProperty = prop;
					}

					if( id.Type.ReturnedClass.IsArray )
					{
						throw new MappingException( "illegal use of an array as an identifier (arrays don't reimplement equals)" );
					}

					MakeIdentifier( subnode, id, mappings );
					break;

				case "composite-id":
					Component compId = new Component( model );
					model.Identifier = compId;
					if( propertyName == null )
					{
						BindComponent( subnode, compId, null, model.Name, "id", false, mappings );
						model.HasEmbeddedIdentifier = compId.IsEmbedded;
						model.IdentifierProperty = null;
					}
					else
					{
						System.Type reflectedClass = GetPropertyType( subnode, mappings, model.MappedClass, propertyName );
						BindComponent( subnode, compId, reflectedClass, model.Name, propertyName, false, mappings );
						Mapping.Property prop = new Mapping.Property( compId );
						BindProperty( subnode, prop, mappings );
						model.IdentifierProperty = prop;
					}
					MakeIdentifier( subnode, compId, mappings );

					System.Type compIdClass = compId.ComponentClass;
					if( !ReflectHelper.OverridesEquals( compIdClass ) )
					{
						throw new MappingException(
							"composite-id class must override Equals(): " + compIdClass.FullName
							);
					}

					if( !ReflectHelper.OverridesGetHashCode( compIdClass ) )
					{
						throw new MappingException(
							"composite-id class must override GetHashCode(): " + compIdClass.FullName
							);
					}

					// Serializability check not ported
					break;

				case "version":
				case "timestamp":
					//VERSION
					SimpleValue val = new SimpleValue( table );
					BindSimpleValue( subnode, val, false, propertyName, mappings );
					if( val.Type == null )
					{
						val.Type = ( ( "version".Equals( name ) ) ? NHibernateUtil.Int32 : NHibernateUtil.Timestamp );
					}
					Mapping.Property timestampProp = new Mapping.Property( val );
					BindProperty( subnode, timestampProp, mappings );
					MakeVersion( subnode, val );
					model.Version = timestampProp;
					model.AddNewProperty( timestampProp );
					break;

				case "discriminator":
					//DISCRIMINATOR
					SimpleValue discrim = new SimpleValue( table );
					model.Discriminator = discrim;
					BindSimpleValue( subnode, discrim, false, RootClass.DefaultDiscriminatorColumnName, mappings );
					if( discrim.Type == null )
					{
						discrim.Type = NHibernateUtil.String;
						foreach( Column col in discrim.ColumnCollection )
						{
							col.Type = NHibernateUtil.String;
							break;
						}
					}
					model.IsPolymorphic = true;
					if( subnode.Attributes[ "force" ] != null && "true".Equals( subnode.Attributes[ "force" ].Value ) )
					{
						model.IsForceDiscriminator = true;
					}
					if( subnode.Attributes[ "insert" ] != null && "false".Equals( subnode.Attributes[ "insert" ].Value ) )
					{
						model.IsDiscriminatorInsertable = false;
					}
					break;

				case "jcs-cache":
				case "cache":
					string className = model.MappedClass.FullName;
					ICacheConcurrencyStrategy cache = CacheFactory.CreateCache( subnode, className, model.IsMutable );
					mappings.AddCache( className, cache );
					model.Cache = cache;

					break;
				}
			}

			model.CreatePrimaryKey( dialect );

			PropertiesFromXML( node, model, mappings );
		}
		/// <remarks>
		/// Called for all collections. <paramref name="containingType" /> parameter
		/// was added in NH to allow for reflection related to generic types.
		/// </remarks>
		public static void BindCollection(XmlNode node, Mapping.Collection model, string className, string path,
		                                  System.Type containingType, Mappings mappings)
		{
			// ROLENAME
			model.Role = StringHelper.Qualify(className, path);
			// TODO: H3.1 has just collection.setRole(path) here - why?

			XmlAttribute inverseNode = node.Attributes["inverse"];
			if (inverseNode != null)
			{
				model.IsInverse = StringHelper.BooleanValue(inverseNode.Value);
			}

			// TODO: H3.1 - not ported: mutable
			XmlAttribute olNode = node.Attributes["optimistic-lock"];
			model.IsOptimisticLocked = olNode == null || "true".Equals(olNode.Value);

			XmlAttribute orderNode = node.Attributes["order-by"];
			if (orderNode != null)
			{
				model.OrderBy = orderNode.Value;
			}
			XmlAttribute whereNode = node.Attributes["where"];
			if (whereNode != null)
			{
				model.Where = whereNode.Value;
			}
			XmlAttribute batchNode = node.Attributes["batch-size"];
			if (batchNode != null)
			{
				model.BatchSize = Int32.Parse(batchNode.Value);
			}

			// PERSISTER
			XmlAttribute persisterNode = node.Attributes["persister"];
			if (persisterNode == null)
			{
				//persister = CollectionPersisterImpl.class;
			}
			else
			{
				model.CollectionPersisterClass = ClassForNameChecked(
					persisterNode.Value, mappings,
					"could not instantiate collection persister class: {0}");
			}

			XmlAttribute typeNode = node.Attributes["collection-type"];
			if (typeNode != null)
			{
				model.TypeName = typeNode.Value;
			}

			// FETCH STRATEGY
			InitOuterJoinFetchSetting(node, model);

			if ("subselect".Equals(XmlHelper.GetAttributeValue(node, "fetch")))
			{
				model.IsSubselectLoadable = true;
				model.Owner.HasSubselectLoadableCollections = true;
			}

			// LAZINESS
			InitLaziness(node, model, mappings, "true", mappings.DefaultLazy);
			// TODO: H3.1 - lazy="extra"

			XmlNode oneToManyNode = node.SelectSingleNode(HbmConstants.nsOneToMany, nsmgr);
			if (oneToManyNode != null)
			{
				OneToMany oneToMany = new OneToMany(model.Owner);
				model.Element = oneToMany;
				BindOneToMany(oneToManyNode, oneToMany, mappings);
				//we have to set up the table later!! yuck
			}
			else
			{
				//TABLE
				XmlAttribute tableNode = node.Attributes["table"];
				string tableName;
				if (tableNode != null)
				{
					tableName = mappings.NamingStrategy.TableName(tableNode.Value);
				}
				else
				{
					tableName = mappings.NamingStrategy.PropertyToTableName(className, path);
				}
				XmlAttribute schemaNode = node.Attributes["schema"];
				string schema = schemaNode == null ? mappings.SchemaName : schemaNode.Value;
				model.CollectionTable = mappings.AddTable(schema, tableName);

				log.Info("Mapping collection: " + model.Role + " -> " + model.CollectionTable.Name);
			}

			//SORT
			XmlAttribute sortedAtt = node.Attributes["sort"];
			// unsorted, natural, comparator.class.name
			if (sortedAtt == null || sortedAtt.Value.Equals("unsorted"))
			{
				model.IsSorted = false;
			}
			else
			{
				model.IsSorted = true;
				if (!sortedAtt.Value.Equals("natural"))
				{
					string comparatorClassName = FullClassName(sortedAtt.Value, mappings);
					try
					{
						model.Comparer = Activator.CreateInstance(ReflectHelper.ClassForName(comparatorClassName));
					}
					catch
					{
						throw new MappingException("could not instantiate comparer class: " + comparatorClassName);
					}
				}
			}

			//ORPHAN DELETE (used for programmer error detection)
			XmlAttribute cascadeAtt = node.Attributes["cascade"];
			if (cascadeAtt != null && cascadeAtt.Value.Equals("all-delete-orphan"))
			{
				model.OrphanDelete = true;
			}


			bool? isGeneric = null;

			XmlAttribute genericAtt = node.Attributes["generic"];
			if (genericAtt != null)
			{
				isGeneric = bool.Parse(genericAtt.Value);
			}

			System.Type collectionType = null;

			if (!isGeneric.HasValue)
			{
				collectionType = GetPropertyType(node, mappings, containingType, GetPropertyName(node));
				isGeneric = collectionType.IsGenericType;
			}

			model.IsGeneric = isGeneric ?? false;

			if (model.IsGeneric)
			{
				// Determine the generic arguments using reflection
				if (collectionType == null)
				{
					collectionType = GetPropertyType(node, mappings, containingType, GetPropertyName(node));
				}
				System.Type[] genericArguments = collectionType.GetGenericArguments();
				model.GenericArguments = genericArguments;
			}
			HandleCustomSQL(node, model);

			//set up second pass
			if (model is List)
			{
				mappings.AddSecondPass(new ListSecondPass(node, mappings, (List) model));
			}
			else if (model is Map)
			{
				mappings.AddSecondPass(new MapSecondPass(node, mappings, (Map) model));
			}
			else if (model is Set)
			{
				mappings.AddSecondPass(new SetSecondPass(node, mappings, (Set) model));
			}
			else if (model is IdentifierCollection)
			{
				mappings.AddSecondPass(new IdentifierCollectionSecondPass(node, mappings, (IdentifierCollection) model));
			}
			else
			{
				mappings.AddSecondPass(new CollectionSecondPass(node, mappings, model));
			}

			foreach (XmlNode filter in node.SelectNodes(HbmConstants.nsFilter, nsmgr))
			{
				ParseFilter(filter, model, mappings);
			}

			XmlNode loader = node.SelectSingleNode(HbmConstants.nsLoader, nsmgr);
			if (loader != null)
			{
				model.LoaderName = XmlHelper.GetAttributeValue(loader, "query-ref");
			}
		}